// import com.alibaba.druid.DbType_ptr;
// import com.alibaba.druid.sql.SQLUtils;
// import com.alibaba.druid.sql.ast.*;
// import com.alibaba.druid.sql.ast.expr->*;
// import com.alibaba.druid.sql.ast.statement.*;
// import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement.TriggerType;
// import com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause;
// import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource.JoinType;
// import com.alibaba.druid.sql.ast.statement.SQLMergeStatement.MergeInsertClause;
// import com.alibaba.druid.sql.ast.statement.SQLMergeStatement.MergeUpdateClause;
// import com.alibaba.druid.sql.dialect.hive.ast.HiveInputOutputFormat;
// import com.alibaba.druid.sql.dialect.hive.stmt->HiveCreateTableStatement;
// import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey;
// import com.alibaba.druid.sql.dialect.mysql.ast.expr->MySqlOrderingExpr;
// import com.alibaba.druid.sql.dialect.mysql.ast.statement.*;
// import com.alibaba.druid.sql.dialect.odps.ast.OdpsNewExpr;
// import com.alibaba.druid.sql.dialect.oracle.ast.OracleSegmentAttributes;
// import com.alibaba.druid.sql.dialect.oracle.ast.expr->OracleCursorExpr;
// import com.alibaba.druid.sql.dialect.oracle.ast.expr->OracleDatetimeExpr;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt->OracleCreatePackageStatement;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt->OracleForStatement;
// import com.alibaba.druid.sql.dialect.oracle.parser.OracleFunctionDataType;
// import com.alibaba.druid.sql.dialect.oracle.parser.OracleProcedureDataType;
// import com.alibaba.druid.util.FnvHash;
// import com.alibaba.druid.util.JdbcUtils;

// import java.io.IOException;
// import java.io.InputStream;
// import java.io.Reader;
// import java.math.BigDecimal;
// import java.math.BigInteger;
// import java.sql.Blob;
// import java.sql.Clob;
// import java.sql.NClob;
// import java.text->SimpleDateFormat;
// import java.util.*;

// import static com.alibaba.druid.util.Utils.getBoolean;

#include "SQLASTOutputVisitor.h"
#include <ctime>
#include "../../DbType.h"
#include "VisitorFeature.h"
#include "ExportParameterVisitorUtils.h"
#include "../SQLUtils.h"
#include "../ast/SQLSetQuantifier.h"
#include "../ast/SQLPartitionBy.h"
#include "../ast/SQLAdhocTableSource.h"
#include "../ast/SQLRecordDataType.h"
#include "../ast/SQLSubPartitionByHash.h"
#include "../ast/SQLSubPartitionByRange.h"
#include "../ast/SQLPartitionByValue.h"
#include "../ast/SQLDeclareItem.h"
#include "../ast/SQLParameter.h"
#include "../ast/SQLUnpivot.h"
#include "../ast/SQLExpr.h"
#include "../ast/SQLName.h"
#include "../ast/SQLSubPartition.h"
#include "../ast/SQLSubPartitionByList.h"
#include "../ast/AutoIncrementType.h"
#include "../ast/SQLKeep.h"
#include "../ast/SQLRowDataType.h"
#include "../ast/SQLStructDataType.h"
#include "../ast/SQLUnionDataType.h"
#include "../ast/SQLMapDataType.h"
#include "../ast/SQLArrayDataType.h"
#include "../ast/SQLPartitionByHash.h"
#include "../ast/SQLPartitionValue.h"
#include "../ast/SQLPartitionSpec.h"
#include "../ast/SQLArgument.h"
#include "../ast/SQLPartition.h"
#include "../ast/SQLPartitionByList.h"
#include "../ast/SQLPartitionByRange.h"
#include "../ast/expr/SQLBinaryOpExprGroup.h"
#include "../ast/expr/SQLNumberExpr.h"
#include "../ast/expr/SQLNullExpr.h"
#include "../ast/expr/SQLAllColumnExpr.h"
#include "../ast/expr/SQLQueryExpr.h"
#include "../ast/expr/SQLPropertyExpr.h"
#include "../ast/expr/SQLExprUtils.h"
#include "../ast/expr/SQLExistsExpr.h"
#include "../ast/expr/SQLCaseStatement.h"
#include "../ast/expr/SQLCastExpr.h"
#include "../ast/expr/SQLNotExpr.h"
#include "../ast/expr/SQLCaseExpr.h"
#include "../ast/expr/SQLBinaryExpr.h"
#include "../ast/expr/SQLContainsExpr.h"
#include "../ast/expr/SQLAggregateExpr.h"
#include "../ast/expr/SQLAggregateOption.h"
#include "../ast/expr/SQLListExpr.h"
#include "../ast/expr/SQLInListExpr.h"
#include "../ast/SQLCurrentTimeExpr.h"
#include "../ast/expr/SQLIntegerExpr.h"
#include "../ast/expr/SQLArrayExpr.h"
#include "../ast/expr/SQLTimeExpr.h"
#include "../ast/expr/SQLDateExpr.h"
#include "../ast/expr/SQLBooleanExpr.h"
#include "../ast/expr/SQLRealExpr.h"
#include "../ast/expr/SQLFloatExpr.h"
#include "../ast/expr/SQLSmallIntExpr.h"
#include "../ast/expr/SQLBigIntExpr.h"
#include "../ast/expr/SQLTinyIntExpr.h"
#include "../ast/expr/SQLDoubleExpr.h"
#include "../ast/expr/SQLDateTimeExpr.h"
#include "../ast/expr/SQLDecimalExpr.h"
#include "../ast/expr/SQLFlashbackExpr.h"
#include "../ast/expr/SQLIntervalUnit.h"
#include "../ast/expr/SQLMatchAgainstExpr.h"
#include "../ast/expr/SQLCurrentOfCursorExpr.h"
#include "../ast/expr/SQLInSubQueryExpr.h"
#include "../ast/expr/SQLAllExpr.h"
#include "../ast/expr/SQLAnyExpr.h"
#include "../ast/expr/SQLSomeExpr.h"
#include "../ast/expr/SQLHexExpr.h"
#include "../ast/expr/SQLNCharExpr.h"
#include "../ast/expr/SQLSizeExpr.h"
#include "../ast/expr/SQLDbLinkExpr.h"
#include "../ast/expr/SQLJSONExpr.h"
#include "../ast/expr/SQLExtractExpr.h"
#include "../ast/expr/SQLValuesExpr.h"
#include "../ast/expr/SQLSequenceExpr.h"
#include "../ast/expr/SQLUnaryExpr.h"
#include "../ast/expr/SQLIntervalExpr.h"
#include "../ast/expr/SQLTimestampExpr.h"
#include "../ast/expr/SQLGroupingSetExpr.h"
#include "../ast/statement/SQLDescribeStatement.h"
#include "../ast/statement/SQLDropMaterializedViewStatement.h"
#include "../ast/statement/SQLForeignKeyImpl.h"
#include "../ast/statement/SQLCheck.h"
#include "../ast/statement/SQLColumnUniqueKey.h"
#include "../ast/statement/SQLCharacterDataType.h"
#include "../ast/statement/SQLAlterTableUnarchivePartition.h"
#include "../ast/statement/SQLColumnReference.h"
#include "../ast/statement/SQLPrimaryKeyImpl.h"
#include "../ast/statement/SQLColumnPrimaryKey.h"
#include "../ast/statement/SQLExportTableStatement.h"
#include "../ast/statement/SQLDropOutlineStatement.h"
#include "../ast/statement/SQLPurgeTableStatement.h"
#include "../ast/statement/SQLShowStatisticListStmt.h"
#include "../ast/statement/SQLShowGrantsStatement.h"
#include "../ast/statement/SQLValuesTableSource.h"
#include "../ast/statement/SQLShowPartitionsStmt.h"
#include "../ast/statement/SQLDumpStatement.h"
#include "../ast/statement/SQLAlterTableSubpartitionAvailablePartitionNum.h"
#include "../ast/statement/SQLWithSubqueryClause_Entry.h"
#include "../ast/statement/SQLColumnCheck.h"
#include "../ast/statement/SQLWithSubqueryClause.h"
#include "../ast/statement/SQLShowStatisticStmt.h"
#include "../ast/statement/SQLImportTableStatement.h"
#include "../ast/statement/SQLShowDatabasesStatement.h"
#include "../ast/statement/SQLAssignItem.h"
#include "../ast/statement/SQLShowFunctionsStatement.h"
#include "../ast/statement/SQLObjectType.h"
#include "../ast/statement/SQLExplainAnalyzeStatement.h"
#include "../ast/statement/SQLCancelJobStatement.h"
#include "../ast/statement/SQLDropUserStatement.h"
#include "../ast/statement/SQLAlterTableDisableConstraint.h"
#include "../ast/statement/SQLShowColumnsStatement.h"
#include "../ast/statement/SQLShowTableGroupsStatement.h"
#include "../ast/statement/SQLShowProcessListStatement.h"
#include "../ast/statement/SQLAlterTableRenameIndex.h"
#include "../ast/statement/SQLAlterTableSetOption.h"
#include "../ast/statement/SQLExplainStatement.h"
#include "../ast/statement/SQLCreateIndexStatement.h"
#include "../ast/statement/SQLShowCreateTableStatement.h"
#include "../ast/statement/SQLAlterTableArchivePartition.h"
#include "../ast/statement/SQLNotNullConstraint.h"
#include "../ast/statement/SQLDropRoleStatement.h"
#include "../ast/statement/SQLCreateRoleStatement.h"
#include "../ast/statement/SQLShowViewsStatement.h"
#include "../ast/statement/SQLShowCreateViewStatement.h"
#include "../ast/statement/SQLAlterTableBlockSize.h"
#include "../ast/statement/SQLAlterTableAlterColumn.h"
#include "../ast/statement/SQLAlterTableDropForeignKey.h"
#include "../ast/statement/SQLAlterTablePartitionCount.h"
#include "../ast/statement/SQLAlterTableDropKey.h"
#include "../ast/statement/SQLDefault.h"
#include "../ast/statement/SQLAlterTableModifyClusteredBy.h"
#include "../ast/statement/SQLAlterTableRenameConstraint.h"
#include "../ast/statement/SQLCreateTableGroupStatement.h"
#include "../ast/statement/SQLExternalRecordFormat.h"
#include "../ast/statement/SQLExprStatement.h"
#include "../ast/statement/SQLDropTableGroupStatement.h"
#include "../ast/statement/SQLUseStatement.h"
#include "../ast/statement/SQLBeginStatement.h"
#include "../ast/statement/SQLShowSessionStatement.h"
#include "../ast/statement/SQLShowCatalogsStatement.h"
#include "../ast/statement/SQLAlterTableReplaceColumn.h"
#include "../ast/statement/SQLAlterTableDropColumnItem.h"
#include "../ast/statement/SQLReleaseSavePointStatement.h"
#include "../ast/statement/SQLAlterSystemGetConfigStatement.h"
#include "../ast/statement/SQLAlterTableMergePartition.h"
#include "../ast/statement/SQLAlterTableGroupStatement.h"
#include "../ast/statement/SQLRollbackStatement.h"
#include "../ast/statement/SQLDropTypeStatement.h"
#include "../ast/statement/SQLDropIndexStatement.h"
#include "../ast/statement/SQLAlterTableDeleteByCondition.h"
#include "../ast/statement/SQLCreateUserStatement.h"
#include "../ast/statement/SQLShowMaterializedViewStatement.h"
#include "../ast/statement/SQLRefreshMaterializedViewStatement.h"
#include "../ast/statement/SQLAlterTableDropIndex.h"
#include "../ast/statement/SQLDropSequenceStatement.h"
#include "../ast/statement/SQLAlterTableAddColumn.h"
#include "../ast/statement/SQLAlterTypeStatement.h"
#include "../ast/statement/SQLAlterFunctionStatement.h"
#include "../ast/statement/SQLCreateMaterializedViewStatement.h"
#include "../ast/statement/SQLAlterMaterializedViewStatement.h"
#include "../ast/statement/SQLAlterTableTruncatePartition.h"
#include "../ast/statement/SQLDropServerStatement.h"
#include "../ast/statement/SQLWhileStatement.h"
#include "../ast/statement/SQLFetchStatement.h"
#include "../ast/statement/SQLCreateTriggerStatement.h"
#include "../ast/statement/SQLUnionQueryTableSource.h"
#include "../ast/statement/SQLCloneTableStatement.h"
#include "../ast/statement/SQLPurgeTemporaryOutputStatement.h"
#include "../ast/statement/SQLAlterSystemSetConfigStatement.h"
#include "../ast/statement/SQLAlterTableRenameColumn.h"
#include "../ast/statement/SQLCommitStatement.h"
#include "../ast/statement/SQLExprHint.h"
#include "../ast/statement/SQLReturnStatement.h"
#include "../ast/statement/SQLDeclareStatement.h"
#include "../ast/statement/SQLAlterTableDiscardPartition.h"
#include "../ast/statement/SQLAlterTableDisableLifecycle.h"
#include "../ast/statement/SQLColumnDefinition.h"
#include "../ast/statement/SQLLoopStatement.h"
#include "../ast/statement/SQLIfStatement.h"
#include "../ast/statement/SQLCloseStatement.h"
#include "../ast/statement/SQLPrivilegeItem.h"
#include "../ast/statement/SQLAlterSequenceStatement.h"
#include "../ast/statement/SQLCreateSequenceStatement.h"
#include "../ast/statement/SQLErrorLoggingClause.h"
#include "../ast/statement/SQLAlterTableRebuildPartition.h"
#include "../ast/statement/SQLAlterTableConvertCharSet.h"
#include "../ast/statement/SQLShowCreateMaterializedViewStatement.h"
#include "../ast/statement/SQLPartitionRef.h"
#include "../ast/statement/SQLAnalyzeTableStatement.h"
#include "../ast/statement/SQLShowIndexesStatement.h"
#include "../ast/statement/SQLAlterIndexStatement.h"
#include "../ast/statement/SQLAlterTableOptimizePartition.h"
#include "../ast/statement/SQLAlterTableAlterIndex.h"
#include "../ast/statement/SQLAlterTableAddIndex.h"
#include "../ast/statement/SQLAlterTableCoalescePartition.h"
#include "../ast/statement/SQLAlterTableAddConstraint.h"
#include "../ast/statement/SQLDropCatalogStatement.h"
#include "../ast/statement/SQLAlterTableExchangePartition.h"
#include "../ast/statement/SQLAlterTableImportPartition.h"
#include "../ast/statement/SQLAlterTableAnalyzePartition.h"
#include "../ast/statement/SQLAlterTableEnableConstraint.h"
#include "../ast/statement/SQLSavePointStatement.h"
#include "../ast/statement/SQLDropProcedureStatement.h"
#include "../ast/statement/SQLDropTableSpaceStatement.h"
#include "../ast/statement/SQLDropFunctionStatement.h"
#include "../ast/statement/SQLAlterTableCheckPartition.h"
#include "../ast/statement/SQLDropDatabaseStatement.h"
#include "../ast/statement/SQLAlterDatabaseItem.h"
#include "../ast/statement/SQLAlterDatabaseStatement.h"
#include "../ast/statement/SQLRevokeStatement.h"
#include "../ast/statement/SQLAlterCharacter.h"
#include "../ast/statement/SQLDeleteStatement.h"
#include "../ast/statement/SQLCallStatement.h"
#include "../ast/statement/SQLExportDatabaseStatement.h"
#include "../ast/statement/SQLAlterViewRenameStatement.h"
#include "../ast/statement/SQLAlterTableAddExtPartition.h"
#include "../ast/statement/SQLAlterTableRepairPartition.h"
#include "../ast/statement/SQLAlterTableRename.h"
#include "../ast/statement/SQLBuildTableStatement.h"
#include "../ast/statement/SQLValuesQuery.h"
#include "../ast/statement/SQLTableLike.h"
#include "../ast/statement/SQLCostStatement.h"
#include "../ast/statement/SQLAlterTableDropPartition.h"
#include "../ast/statement/SQLShowTablesStatement.h"
#include "../ast/statement/SQLCreateOutlineStatement.h"
#include "../ast/statement/SQLCreateProcedureStatement.h"
#include "../ast/statement/SQLAlterTableAddSupplemental.h"
#include "../ast/statement/SQLShowOutlinesStatement.h"
#include "../ast/statement/SQLAlterOutlineStatement.h"
#include "../ast/statement/SQLDropOfflineModelStatement.h"
#include "../ast/statement/SQLDropSynonymStatement.h"
#include "../ast/statement/SQLTruncateStatement.h"
#include "../ast/statement/SQLShowQueryTaskStatement.h"
#include "../ast/statement/SQLAlterTableRenamePartition.h"
#include "../ast/statement/SQLPurgeLogsStatement.h"
#include "../ast/statement/SQLCopyFromStatement.h"
#include "../ast/statement/SQLSyncMetaStatement.h"
#include "../ast/statement/SQLImportDatabaseStatement.h"
#include "../ast/statement/SQLSubqueryTableSource.h"
#include "../ast/statement/SQLRenameUserStatement.h"
#include "../ast/statement/SQLSubmitJobStatement.h"
#include "../ast/statement/SQLRestoreStatement.h"
#include "../ast/statement/SQLArchiveTableStatement.h"
#include "../ast/statement/SQLBackupStatement.h"
#include "../ast/statement/SQLShowHistoryStatement.h"
#include "../ast/statement/SQLSelectOrderByItem.h"
#include "../ast/statement/SQLOptimizeStatement.h"
#include "../ast/statement/SQLInsertStatement.h"
#include "../ast/statement/SQLGrantStatement.h"
#include "../ast/statement/SQLJoinTableSource.h"
#include "../ast/statement/SQLNullConstraint.h"
#include "../ast/statement/SQLMergeStatement.h"
#include "../ast/statement/SQLAlterViewStatement.h"
#include "../ast/statement/SQLSelectItem.h"
#include "../ast/statement/SQLSelect.h"
#include "../ast/statement/SQLUnionQuery.h"
#include "../ast/statement/SQLUnionOperator.h"
#include "../ast/statement/SQLLateralViewTableSource.h"
#include "../ast/statement/SQLAlterTableStatement.h"
#include "../ast/statement/SQLUpdateSetItem.h"
#include "../ast/statement/SQLSelectStatement.h"
#include "../ast/statement/SQLDropTableStatement.h"
#include "../ast/statement/SQLDropViewStatement.h"
#include "../ast/statement/SQLDropLogFileGroupStatement.h"
#include "../ast/statement/SQLSetStatement.h"
#include "../ast/statement/SQLCreateViewStatement.h"
#include "../ast/statement/SQLCreateDatabaseStatement.h"
#include "../ast/statement/SQLDropEventStatement.h"
#include "../ast/statement/SQLDropResourceStatement.h"
#include "../ast/statement/SQLDropResourceGroupStatement.h"
#include "../ast/statement/SQLDropTriggerStatement.h"
#include "../ast/statement/SQLCommentStatement.h"
#include "../ast/statement/SQLAlterTableDropConstraint.h"
#include "../ast/statement/SQLAlterTableItem.h"
#include "../ast/statement/SQLUpdateStatement.h"
#include "../ast/statement/SQLOpenStatement.h"
#include "../ast/statement/SQLBlockStatement.h"
#include "../ast/statement/SQLCreateFunctionStatement.h"
#include "../ast/statement/SQLAlterTableDropExtPartition.h"
#include "../ast/statement/SQLAlterTableReOrganizePartition.h"
#include "../ast/statement/SQLAlterTableSetLocation.h"
#include "../ast/statement/SQLAlterTableSetLifecycle.h"
#include "../ast/statement/SQLAlterTableSetComment.h"
#include "../ast/statement/SQLAlterTableEnableLifecycle.h"
#include "../ast/statement/SQLAlterTablePartitionSetProperties.h"
#include "../ast/statement/SQLUnnestTableSource.h"
#include "../ast/statement/SQLAlterTableTouch.h"
#include "../ast/statement/SQLAlterTableArchive.h"
#include "../ast/statement/SQLAlterTableAddPartition.h"
#include "../ast/statement/SQLAlterTablePartition.h"
#include "../dialect/hive/ast/HiveInputOutputFormat.h"
#include "../dialect/hive/stmt/HiveCreateTableStatement.h"
#include "../dialect/mysql/ast/MySqlPrimaryKey.h"
#include "../dialect/mysql/ast/expr/MySqlOrderingExpr.h"
#include "../dialect/mysql/ast/statement/MySqlCreateTableStatement.h"
#include "../dialect/mysql/ast/statement/SQLCreateResourceGroupStatement.h"
#include "../dialect/mysql/ast/statement/MySqlAlterDatabaseKillJob.h"
#include "../dialect/mysql/ast/statement/MySqlKillStatement.h"
#include "../dialect/mysql/ast/statement/MySqlAlterDatabaseSetOption.h"
#include "../dialect/mysql/ast/statement/SQLAlterResourceGroupStatement.h"
#include "../dialect/mysql/ast/statement/MySqlExtPartition.h"
#include "../dialect/odps/ast/OdpsNewExpr.h"
#include "../dialect/oracle/ast/OracleSegmentAttributes.h"
#include "../dialect/oracle/ast/expr/OracleCursorExpr.h"
#include "../dialect/oracle/ast/expr/OracleDatetimeExpr.h"
#include "../dialect/oracle/ast/stmt/OracleCreatePackageStatement.h"
#include "../dialect/oracle/ast/stmt/OracleForStatement.h"
#include "../dialect/oracle/parser/OracleFunctionDataType.h"
#include "../dialect/oracle/parser/OracleProcedureDataType.h"
#include "../../utils/FnvHash.h"
#include "../../utils/instanceof.h"
#include "../../utils/StringUtils.h"
#include "../../utils/log.h"
#include "../../utils/doubleEqual.h"
#include "../../utils/listAddAll.h"
#include "../../utils/listTran.h"
#include "../../utils/JdbcUtils.h"
#include "../../Exception/RuntimeException.h"
#include "../../Exception/UnsupportedOperationException.h"

int SQLASTOutputVisitor::ONE = 1;
BOOL_ptr SQLASTOutputVisitor::defaultPrintStatementAfterSemi = BOOL::FALSE;
std::shared_ptr<std::vector<string_ptr>> SQLASTOutputVisitor::variantValuesCache_1 = std::make_shared<std::vector<string_ptr>>();
std::shared_ptr<std::vector<string_ptr>> SQLASTOutputVisitor::variantValuesCache = std::make_shared<std::vector<string_ptr>>();

BOOL_ptr SQLASTOutputVisitor::__init = SQLASTOutputVisitor::init();
BOOL_ptr SQLASTOutputVisitor::init()
{
  for (int i = 0; i < 64; i++)
  {
    variantValuesCache_1->push_back(make_string_ptr(""));
  }
  for (int len = 0; len < 64; ++len)
  {
    std::stringstream buf;
    buf << '(';
    for (int i = 0; i < len; ++i)
    {
      if (i != 0)
      {
        if (i % 5 == 0)
        {
          buf << "\n\t";
        }
        buf << ", ";
      }
      buf << '?';
    }
    buf << ')';
    variantValuesCache_1->at(len) = make_string_ptr(buf.str());
  }

  for (int i = 0; i < 64; i++)
  {
    variantValuesCache->push_back(make_string_ptr(""));
  }
  for (int len = 0; len < 64; ++len)
  {
    std::stringstream buf;
    buf << '(';
    for (int i = 0; i < len; ++i)
    {
      if (i != 0)
      {
        if (i % 5 == 0)
        {
          buf << "\n\t\t";
        }
        buf << ", ";
      }
      buf << '?';
    }
    buf << ')';
    variantValuesCache->at(len) = make_string_ptr(buf.str());
  }
}

SQLASTOutputVisitor::SQLASTOutputVisitor(std::stringstream *appender)
{
  this->appender = appender;
  selectListNumberOfLine = 5;
  tableMapping = std::make_shared<std::map<std::string, string_ptr>>();
  quote = '"';
  printStatementAfterSemi = SQLASTOutputVisitor::defaultPrintStatementAfterSemi;
  features |= VisitorFeature::OutputPrettyFormat.mask;
  parameters = std::make_shared<std::list<Object_ptr>>();
  inputParameters = std::make_shared<std::list<Object_ptr>>();
  tables = std::make_shared<std::set<string_ptr>>();
}

SQLASTOutputVisitor::SQLASTOutputVisitor(std::stringstream *appender, DbType_ptr dbType)
{
  this->appender = appender;
  this->dbType = dbType;
  selectListNumberOfLine = 5;
  tableMapping = std::make_shared<std::map<std::string, string_ptr>>();
  quote = '"';
  printStatementAfterSemi = SQLASTOutputVisitor::defaultPrintStatementAfterSemi;
  features |= VisitorFeature::OutputPrettyFormat.mask;
  parameters = std::make_shared<std::list<Object_ptr>>();
  inputParameters = std::make_shared<std::list<Object_ptr>>();
  tables = std::make_shared<std::set<string_ptr>>();
}

SQLASTOutputVisitor::SQLASTOutputVisitor(std::stringstream *appender, BOOL_ptr parameterized)
{
  this->appender = appender;
  this->config(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterized)), parameterized);

  selectListNumberOfLine = 5;
  tableMapping = std::make_shared<std::map<std::string, string_ptr>>();
  quote = '"';
  printStatementAfterSemi = SQLASTOutputVisitor::defaultPrintStatementAfterSemi;
  features |= VisitorFeature::OutputPrettyFormat.mask;
  parameters = std::make_shared<std::list<Object_ptr>>();
  inputParameters = std::make_shared<std::list<Object_ptr>>();
  tables = std::make_shared<std::set<string_ptr>>();
}

int SQLASTOutputVisitor::getReplaceCount()
{
  return this->replaceCount;
}

void SQLASTOutputVisitor::incrementReplaceCunt()
{
  replaceCount++;
}

time_t SQLASTOutputVisitor::getTimeZone()
{
  return timeZone;
}

void SQLASTOutputVisitor::setTimeZone(time_t timeZone)
{
  this->timeZone = timeZone;
}

void SQLASTOutputVisitor::addTableMapping(string_ptr srcTable, string_ptr destTable)
{
  if (tableMapping == nullptr)
  {
    tableMapping = std::make_shared<std::map<std::string, string_ptr>>();
  }

  if (srcTable->find('.') != std::string::npos)
  {
    SQLExpr_ptr expr = SQLUtils::toSQLExpr(srcTable, dbType);
    if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
    {
      srcTable = (std::dynamic_pointer_cast<SQLPropertyExpr>(expr))->simplify()->toString();
    }
  }
  else
  {
    srcTable = SQLUtils::normalize(srcTable);
  }
  tableMapping->insert(std::make_pair(srcTable->c_str(), destTable));
}

void SQLASTOutputVisitor::setTableMapping(std::shared_ptr<std::map<std::string, string_ptr>> tableMapping)
{
  this->tableMapping = tableMapping;
}

Object_list_ptr SQLASTOutputVisitor::getParameters()
{
  if (parameters == nullptr)
  {
    parameters = std::make_shared<std::list<Object_ptr>>();
  }

  return parameters;
}

BOOL_ptr SQLASTOutputVisitor::isDesensitize()
{
  return SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputDesensitize)));
}

void SQLASTOutputVisitor::setDesensitize(BOOL_ptr desensitize)
{
  config(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputDesensitize)), desensitize);
}

std::shared_ptr<std::set<string_ptr>> SQLASTOutputVisitor::getTables()
{
  if (this->table != nullptr &&
      this->tables == nullptr)
  {
    std::shared_ptr<std::set<string_ptr>> tmp = std::make_shared<std::set<string_ptr>>();
    tmp->insert(this->table);
    return tmp;
  }
  return this->tables;
}

// @Deprecated
void SQLASTOutputVisitor::setParameters(Object_list_ptr parameters)
{
  if (parameters != nullptr &&
      parameters->size() > 0)
  {
    this->inputParameters = parameters;
  }
  else
  {
    this->parameters = parameters;
  }
}

void SQLASTOutputVisitor::setInputParameters(Object_list_ptr parameters)
{
  this->inputParameters = parameters;
}

/**
 * @since 1.1.5
 */
void SQLASTOutputVisitor::setOutputParameters(Object_list_ptr parameters)
{
  this->parameters = parameters;
}

int SQLASTOutputVisitor::getIndentCount()
{
  return indentCount;
}

std::stringstream *SQLASTOutputVisitor::getAppender()
{
  return appender;
}

BOOL_ptr SQLASTOutputVisitor::isPrettyFormat()
{
  return SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputPrettyFormat)));
}

void SQLASTOutputVisitor::setPrettyFormat(BOOL_ptr prettyFormat)
{
  config(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputPrettyFormat)), prettyFormat);
}

void SQLASTOutputVisitor::decrementIndent()
{
  this->indentCount--;
}

void SQLASTOutputVisitor::incrementIndent()
{
  this->indentCount++;
}

BOOL_ptr SQLASTOutputVisitor::isParameterized()
{
  return SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterized)));
}

void SQLASTOutputVisitor::setParameterized(BOOL_ptr parameterized)
{
  config(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterized)), parameterized);
}

BOOL_ptr SQLASTOutputVisitor::isParameterizedMergeInList()
{
  return parameterizedMergeInList;
}

void SQLASTOutputVisitor::setParameterizedMergeInList(BOOL_ptr parameterizedMergeInList)
{
  this->parameterizedMergeInList = parameterizedMergeInList;
}

BOOL_ptr SQLASTOutputVisitor::isParameterizedQuesUnMergeInList()
{
  return parameterizedQuesUnMergeInList;
}

void SQLASTOutputVisitor::setParameterizedQuesUnMergeInList(BOOL_ptr parameterizedQuesUnMergeInList)
{
  config(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterizedQuesUnMergeInList)), parameterizedQuesUnMergeInList);
}

BOOL_ptr SQLASTOutputVisitor::isExportTables()
{
  return exportTables;
}

void SQLASTOutputVisitor::setExportTables(BOOL_ptr exportTables)
{
  this->exportTables = exportTables;
}

void SQLASTOutputVisitor::print(char value)
{
  if (this->appender == nullptr)
  {
    return;
  }

  // try {
  (*this->appender) << value;
  // } catch (IOException e) {
  //     throw new RuntimeException("print error", e);
  // }
}

void SQLASTOutputVisitor::printChar(char value)
{
  if (this->appender == nullptr)
  {
    return;
  }

  // try {
  (*this->appender) << value;
  // } catch (IOException e) {
  //     throw new RuntimeException("print error", e);
  // }
}

void SQLASTOutputVisitor::print(int value)
{
  if (this->appender == nullptr)
  {
    return;
  }

  (*this->appender) << value;
  // if (appender instanceof StringBuffer) {
  //     ((StringBuffer) appender).append(value);
  // } else if (appender instanceof StringBuilder) {
  //     ((StringBuilder) appender).append(value);
  // } else {
  //     print0(int.toString(value));
  // }
}

void SQLASTOutputVisitor::print(long value)
{
  if (this->appender == nullptr)
  {
    return;
  }

  (*this->appender) << value;
  // if (appender instanceof StringBuilder) {
  //     ((StringBuilder) appender).append(value);
  // } else if (appender instanceof StringBuffer) {
  //     ((StringBuffer) appender).append(value);
  // } else {
  //     print0(Long.toString(value));
  // }
}

void SQLASTOutputVisitor::print(float value)
{
  if (this->appender == nullptr)
  {
    return;
  }

  (*this->appender) << value;
  // if (appender instanceof StringBuilder) {
  //     ((StringBuilder) appender).append(value);
  // } else if (appender instanceof StringBuffer) {
  //     ((StringBuffer) appender).append(value);
  // } else {
  //     print0(Float.toString(value));
  // }
}

void SQLASTOutputVisitor::print(double value)
{
  if (this->appender == nullptr)
  {
    return;
  }

  (*this->appender) << value;
  // if (appender instanceof StringBuilder) {
  //     ((StringBuilder) appender).append(value);
  // } else if (appender instanceof StringBuffer) {
  //     ((StringBuffer) appender).append(value);
  // } else {
  //     print0(Double.toString(value));
  // }
}

void SQLASTOutputVisitor::print_time(time_t date)
{
  if (this->appender == nullptr)
  {
    return;
  }

  // SimpleDateFormat dateFormat;
  // if (date instanceof java.sql.time_t) {
  //     dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  //     print0("DATE ");
  // } else if (date instanceof java.sql.Time) {
  //     dateFormat = new SimpleDateFormat("HH:mm:ss");
  //     print0("TIME ");
  // } else {
  //     dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
  //     print0("TIMESTAMP ");
  // }

  // if (timeZone != nullptr) {
  //     dateFormat.setTimeZone(timeZone);
  // }
  struct tm info;
  char buffer[80];
  localtime_r(&date, &info);
  strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", &info);

  std::string tmp = "'";
  tmp += buffer;
  tmp += "'";
  print0(make_string_ptr(tmp));
}

void SQLASTOutputVisitor::print(string_ptr text)
{
  if (this->appender == nullptr)
  {
    return;
  }
  print0(text);
}

void SQLASTOutputVisitor::print0(string_ptr text)
{
  if (appender == nullptr)
  {
    return;
  }

  // try {
  (*this->appender) << text;
  // } catch (IOException e) {
  //     throw new RuntimeException("println error", e);
  // }
}

void SQLASTOutputVisitor::printUcase(string_ptr text)
{
  print0(ucase ? StringUtils::toUpper(text) : StringUtils::toLower(text));
}

void SQLASTOutputVisitor::printName0(string_ptr text)
{
  if (text->length() == 0)
  {
    return;
  }

  try
  {
    if (printNameQuote)
    {
      char c0 = text->at(0);
      if (c0 == quote)
      {
        (*this->appender) << text;
      }
      else if (c0 == '"' && text->at(text->length() - 1) == '"')
      {
        (*this->appender) << quote;
        (*this->appender) << text->substr(1, text->length() - 1);
        (*this->appender) << quote;
      }
      else if (c0 == '`' && text->at(text->length() - 1) == '`')
      {
        (*this->appender) << quote;
        (*this->appender) << text->substr(1, text->length() - 1);
        (*this->appender) << quote;
      }
      else
      {
        (*this->appender) << quote;
        (*this->appender) << text;
        (*this->appender) << quote;
      }
    }
    else
    {
      (*this->appender) << text;
    }
    // } catch (IOException e) {
  }
  catch (const std::exception &e)
  {
    std::string tmp = "println error";
    tmp += e.what();
    throw new RuntimeException(make_string_ptr(tmp));
  }
  catch (...)
  {
    LOG_INFO << "catch";
  }
}

void SQLASTOutputVisitor::printAlias(string_ptr alias)
{
  if (alias == nullptr || alias->length() == 0)
  {
    return;
  }

  print(' ');

  try
  {
    (*this->appender) << alias;
  }
  catch (const std::exception &e)
  {
    std::string tmp = "println error";
    tmp += e.what();
    throw new RuntimeException(make_string_ptr(tmp));
  }
  catch (...)
  {
    LOG_INFO << "catch";
  }
}

void SQLASTOutputVisitor::printAndAccept(SQLObject_list_ptr nodes, string_ptr seperator)
{
  auto it_nodes = nodes->begin();
  for (int i = 0, size = nodes->size(); i < size; ++i)
  {
    if (i != 0)
    {
      print0(seperator);
    }
    std::advance(it_nodes, i);
    (*it_nodes)->accept(SharedObject(SQLASTOutputVisitor));
  }
}

void SQLASTOutputVisitor::printAndAccept(SQLExpr_list_ptr nodes, string_ptr seperator, BOOL_ptr parameterized)
{
  auto it_nodes = nodes->begin();
  for (int i = 0, size = nodes->size(); i < size; ++i)
  {
    if (i != 0)
    {
      print0(seperator);
    }
    std::advance(it_nodes, i);
    printExpr((*it_nodes), parameterized);
  }
}

int SQLASTOutputVisitor::paramCount(SQLExpr_ptr x)
{
  if (instanceof <SQLExpr, SQLName>(x))
  {
    return 1;
  }

  if (instanceof <SQLExpr, SQLAggregateExpr>(x))
  {
    SQLAggregateExpr_ptr aggregateExpr = std::dynamic_pointer_cast<SQLAggregateExpr>(x);
    SQLExpr_list_ptr args = aggregateExpr->getArguments();
    int paramCount_ = 1;
    for (SQLExpr_ptr arg : *args)
    {
      paramCount_ += paramCount(arg);
    }
    if (aggregateExpr->getOver() != nullptr)
    {
      paramCount_ += 1;
    }
    return paramCount_;
  }

  if (instanceof <SQLExpr, SQLMethodInvokeExpr>(x))
  {
    SQLExpr_list_ptr params = (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(x))->getArguments();
    int paramCount_ = 1;
    for (SQLExpr_ptr param : *params)
    {
      paramCount_ += paramCount(param);
    }
    return paramCount_;
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(x))
  {
    return paramCount((std::dynamic_pointer_cast<SQLBinaryOpExpr>(x))->getLeft()) + paramCount((std::dynamic_pointer_cast<SQLBinaryOpExpr>(x))->getRight());
  }

  if (instanceof <SQLExpr, SQLCaseExpr>(x))
  {
    return 10;
  }

  return 1;
}

void SQLASTOutputVisitor::printSelectList(SQLSelectItem_list_ptr selectList)
{
  this->indentCount++;
  auto it_selectList = selectList->begin();
  for (int i = 0, lineItemCount = 0, size = selectList->size(); i < size; ++i, ++lineItemCount)
  {
    std::advance(it_selectList, i);
    SQLSelectItem_ptr selectItem = *it_selectList;
    SQLExpr_ptr selectItemExpr = selectItem->getExpr();

    int paramCount_ = paramCount(selectItemExpr);

    bool methodOrBinary = (!(instanceof <SQLExpr, SQLName>(selectItemExpr))) && (instanceof <SQLExpr, SQLMethodInvokeExpr>(selectItemExpr) || instanceof <SQLExpr, SQLAggregateExpr>(selectItemExpr) || instanceof <SQLExpr, SQLBinaryOpExpr>(selectItemExpr));

    if (methodOrBinary)
    {
      lineItemCount += (paramCount_ - 1);
    }

    if (i != 0)
    {
      std::advance(it_selectList, i - 1);
      SQLSelectItem_ptr preSelectItem = *it_selectList;
      if (preSelectItem->getAfterCommentsDirect() != nullptr)
      {
        lineItemCount = 0;
        println();
      }
      else if (methodOrBinary)
      {
        if (lineItemCount >= selectListNumberOfLine)
        {
          lineItemCount = paramCount_;
          println();
        }
      }
      else if (lineItemCount >= selectListNumberOfLine || 
      instanceof <SQLExpr, SQLQueryExpr>(selectItemExpr) ||
       instanceof <SQLExpr, SQLCaseExpr>(selectItemExpr) ||
        (instanceof <SQLExpr, SQLCharExpr>(selectItemExpr) && (std::dynamic_pointer_cast<SQLCharExpr>(selectItemExpr))->getText()->length() > 20))
      {
        lineItemCount = 0;
        println();
      }

      print0(make_string_ptr(", "));
    }

    if (selectItem->getClass()->c_str() == SQLSelectItem::class_->c_str())
    {
      this->visit(selectItem);
    }
    else
    {
      selectItem->accept(SharedObject(SQLASTOutputVisitor));
    }

    if (selectItem->hasAfterComment())
    {
      print(' ');
      printlnComment(selectItem->getAfterCommentsDirect());
    }
  }
  this->indentCount--;
}

void SQLASTOutputVisitor::printlnAndAccept(SQLObject_list_ptr nodes, string_ptr seperator)
{
  auto it_nodes = nodes->begin();
  for (int i = 0, size = nodes->size(); i < size; ++i)
  {
    if (i != 0)
    {
      println(seperator);
    }

    std::advance(it_nodes, i);
    (std::dynamic_pointer_cast<SQLObject>(*it_nodes))->accept(SharedObject(SQLASTOutputVisitor));
  }
}

void SQLASTOutputVisitor::printIndent()
{
  if (this->appender == nullptr)
  {
    return;
  }

  try
  {
    for (int i = 0; i < this->indentCount; ++i)
    {
      (*this->appender) << '\t';
    }
  }
  catch (const std::exception &e)
  {
    std::string tmp = "println error";
    tmp += e.what();
    throw new RuntimeException(make_string_ptr(tmp));
  }
  catch (...)
  {
    LOG_INFO << "catch";
  }
}

void SQLASTOutputVisitor::println()
{
  if (!isPrettyFormat())
  {
    print(' ');
    return;
  }

  print('\n');
  lines++;
  printIndent();
}

void SQLASTOutputVisitor::println(std::string text)
{
  println(make_string_ptr(text));
}

void SQLASTOutputVisitor::println(string_ptr text)
{
  print(text);
  println();
}

// ////////////////////

bool SQLASTOutputVisitor::visit(SQLBetweenExpr_ptr x)
{
  SQLExpr_ptr testExpr = x->getTestExpr();
  SQLExpr_ptr beginExpr = x->getBeginExpr();
  SQLExpr_ptr endExpr = x->getEndExpr();

  BOOL_ptr quote = BOOL::FALSE;
  if (instanceof <SQLExpr, SQLBinaryOpExpr>(testExpr))
  {
    SQLBinaryOperator_ptr operator_ = (std::dynamic_pointer_cast<SQLBinaryOpExpr>(testExpr))->getOperator();
    if (operator_->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str() ||
        operator_->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str() ||
        operator_->name->c_str() == SQLBinaryOperator::BooleanXor.name->c_str() ||
        operator_->name->c_str() == SQLBinaryOperator::Assignment.name->c_str())
    {
      quote = BOOL::TRUE;
    }
    else
    {
      quote = (std::dynamic_pointer_cast<SQLBinaryOpExpr>(testExpr))->isParenthesized();
    }
  }
  else if (instanceof <SQLExpr, SQLInListExpr>(testExpr) || 
  instanceof <SQLExpr, SQLBetweenExpr>(testExpr) || 
  instanceof <SQLExpr, SQLNotExpr>(testExpr) || 
  instanceof <SQLExpr, SQLUnaryExpr>(testExpr) || 
  instanceof <SQLExpr, SQLCaseExpr>(testExpr) || 
  instanceof <SQLExpr, SQLBinaryOpExprGroup>(testExpr))
  {
    quote = BOOL::TRUE;
  }

  if (testExpr != nullptr)
  {
    if (quote)
    {
      print('(');
      printExpr(testExpr, parameterized);
      print(')');
    }
    else
    {
      printExpr(testExpr, parameterized);
    }
  }

  if (x->isNot())
  {
    print0(ucase ? make_string_ptr(" NOT BETWEEN ") : make_string_ptr(" not between "));
  }
  else
  {
    print0(ucase ? make_string_ptr(" BETWEEN ") : make_string_ptr(" between "));
  }

  int lines = this->lines;
  if (instanceof <SQLExpr, SQLBinaryOpExpr>(beginExpr))
  {
    SQLBinaryOpExpr_ptr binaryOpBegin = std::dynamic_pointer_cast<SQLBinaryOpExpr>(beginExpr);
    incrementIndent();
    if (binaryOpBegin->isParenthesized() || 
    binaryOpBegin->getOperator()->isLogical() || 
    binaryOpBegin->getOperator()->isRelational())
    {
      print('(');
      printExpr(beginExpr, parameterized);
      print(')');
    }
    else
    {
      printExpr(beginExpr, parameterized);
    }
    decrementIndent();
  }
  else if (instanceof <SQLExpr, SQLInListExpr>(beginExpr) ||
   instanceof <SQLExpr, SQLBetweenExpr>(beginExpr) ||
    instanceof <SQLExpr, SQLNotExpr>(beginExpr) || 
    instanceof <SQLExpr, SQLUnaryExpr>(beginExpr) ||
     instanceof <SQLExpr, SQLCaseExpr>(beginExpr) || 
     instanceof <SQLExpr, SQLBinaryOpExprGroup>(beginExpr))
  {
    print('(');
    printExpr(beginExpr, parameterized);
    print(')');
  }
  else
  {
    printExpr(beginExpr, parameterized);
  }

  if (lines != this->lines)
  {
    println();
    print0(ucase ? make_string_ptr("AND ") : make_string_ptr("and "));
  }
  else
  {
    print0(ucase ? make_string_ptr(" AND ") : make_string_ptr(" and "));
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(endExpr))
  {
    SQLBinaryOpExpr_ptr binaryOpEnd = std::dynamic_pointer_cast<SQLBinaryOpExpr>(endExpr);
    incrementIndent();
    if (binaryOpEnd->isParenthesized() || binaryOpEnd->getOperator()->isLogical() || binaryOpEnd->getOperator()->isRelational())
    {
      print('(');
      printExpr(endExpr, parameterized);
      print(')');
    }
    else
    {
      printExpr(endExpr, parameterized);
    }
    decrementIndent();
  }
  else if (instanceof <SQLExpr, SQLInListExpr>(endExpr) ||
   instanceof <SQLExpr, SQLBetweenExpr>(endExpr) ||
    instanceof <SQLExpr, SQLNotExpr>(endExpr) ||
     instanceof <SQLExpr, SQLUnaryExpr>(endExpr) ||
      instanceof <SQLExpr, SQLCaseExpr>(endExpr) ||
       instanceof <SQLExpr, SQLBinaryOpExprGroup>(endExpr))
  {
    print('(');
    printExpr(endExpr, parameterized);
    print(')');
  }
  else
  {
    printExpr(endExpr, parameterized);
  }

  if (x->getHint() != nullptr)
  {
    x->getHint()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLBinaryOpExprGroup_ptr x)
{
  SQLObject_ptr parent = x->getParent();
  SQLBinaryOperator_ptr operator_ = x->getOperator();

  bool isRoot = instanceof <SQLObject, SQLSelectQueryBlock>(parent) ||
      instanceof <SQLObject, SQLBinaryOpExprGroup>(parent);

  SQLExpr_list_ptr items = x->getItems();
  if (items->empty())
  {
    print(make_string_ptr("BOOL::TRUE"));
    return false;
  }

  if (isRoot)
  {
    this->indentCount++;
  }

  if (this->parameterized)
  {
    SQLExpr_ptr firstLeft = nullptr;
    SQLBinaryOperator_ptr firstOp = nullptr;
    Object_list_ptr parameters = std::make_shared<std::list<Object_ptr>>();

    SQLBinaryOpExpr_list_ptr literalItems = nullptr;

    if ((operator_->name->c_str() != SQLBinaryOperator::BooleanOr.name->c_str() ||
         !SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterizedQuesUnMergeOr)))) &&
        (operator_->name->c_str() != SQLBinaryOperator::BooleanAnd.name->c_str() ||
         !SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterizedQuesUnMergeAnd)))))
    {
      auto it_items = items->begin();
      for (int i = 0; i < items->size(); i++)
      {
        std::advance(it_items, i);
        SQLExpr_ptr item = *it_items;
        if (instanceof <SQLExpr, SQLBinaryOpExpr>(item))
        {
          SQLBinaryOpExpr_ptr binaryItem = std::dynamic_pointer_cast<SQLBinaryOpExpr>(item);
          SQLExpr_ptr left = binaryItem->getLeft();
          SQLExpr_ptr right = binaryItem->getRight();

          if (instanceof <SQLExpr, SQLLiteralExpr>(right) &&
                             !(instanceof <SQLExpr, SQLNullExpr>(right)))
          {
            if (instanceof <SQLExpr, SQLLiteralExpr>(left))
            {
              if (literalItems == nullptr)
              {
                literalItems = std::make_shared<std::list<SQLBinaryOpExpr_ptr>>();
              }
              literalItems->push_back(binaryItem);
              continue;
            }

            if (this->parameters != nullptr)
            {
              ExportParameterVisitorUtils::exportParameter(parameters, right);
            }
          }
          else if (instanceof <SQLExpr, SQLVariantRefExpr>(right))
          {
            // skip
          }
          else
          {
            firstLeft = nullptr;
            break;
          }

          if (firstLeft == nullptr)
          {
            firstLeft = binaryItem->getLeft();
            firstOp = binaryItem->getOperator();
          }
          else
          {
            if (firstOp != binaryItem->getOperator() ||
                !SQLExprUtils::equals(firstLeft, left))
            {
              firstLeft = nullptr;
              break;
            }
          }
        }
        else
        {
          firstLeft = nullptr;
          break;
        }
      }
    }

    if (firstLeft != nullptr)
    {
      if (literalItems != nullptr)
      {
        for (SQLBinaryOpExpr_ptr literalItem : *literalItems)
        {
          visit(literalItem);
          println();
          printOperator(operator_);
          print(' ');
        }
      }
      printExpr(firstLeft, parameterized);
      print(' ');
      printOperator(firstOp);
      print0(make_string_ptr(" ?"));

      if (this->parameters != nullptr)
      {
        if (parameters->size() > 0)
        {
          // this->parameters.addAll(parameters);
          for (auto it : *parameters)
          {
            this->parameters->push_back(it);
          }
        }
      }

      incrementReplaceCunt();
      if (isRoot)
      {
        this->indentCount--;
      }
      return false;
    }
  }

  auto it_items = items->begin();
  for (int i = 0; i < items->size(); i++)
  {
    std::advance(it_items, i);
    SQLExpr_ptr item = *it_items;

    if (i != 0)
    {
      println();
      printOperator(operator_);
      print(' ');
    }

    if (item->hasBeforeComment())
    {
      printlnComments(item->getBeforeCommentsDirect());
    }

    if (instanceof <SQLExpr, SQLBinaryOpExpr>(item))
    {
      SQLBinaryOpExpr_ptr binaryOpExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(item);
      SQLExpr_ptr binaryOpExprRight = binaryOpExpr->getRight();
      SQLBinaryOperator_ptr itemOp = binaryOpExpr->getOperator();

      bool isLogic = itemOp->isLogical();
      if (isLogic)
      {
        indentCount++;
      }

      bool bracket;
      if (itemOp->priority > operator_->priority)
      {
        bracket = true;
      }
      else
      {
        bracket = ((*binaryOpExpr->isParenthesized()) == BOOL::TRUE) & !((*parameterized) == BOOL::TRUE);
      }
      if (bracket)
      {
        print('(');
        visit(binaryOpExpr);
        print(')');
      }
      else
      {
        visit(binaryOpExpr);
      }

      if (item->hasAfterComment() && !parameterized)
      {
        print(' ');
        printlnComment(item->getAfterCommentsDirect());
      }

      if (isLogic)
      {
        indentCount--;
      }
    }
    else if (instanceof <SQLExpr, SQLBinaryOpExprGroup>(item))
    {
      print('(');
      visit(std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(item));
      print(')');
    }
    else
    {
      printExpr(item, parameterized);
    }
  }
  if (isRoot)
  {
    this->indentCount--;
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLBinaryOpExpr_ptr x)
{
  SQLBinaryOperator_ptr operator_ = x->getOperator();
  if (this->parameterized &&
      operator_->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str() &&
      !SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterizedQuesUnMergeOr))))
  {
    x = SQLBinaryOpExpr::merge(SharedObject(SQLASTOutputVisitor), x);

    operator_ = x->getOperator();
  }

  if (inputParameters != nullptr &&
          inputParameters->size() > 0 &&
          operator_->name->c_str() == SQLBinaryOperator::Equality.name->c_str() &&
      instanceof <SQLExpr, SQLVariantRefExpr>(x->getRight()))
  {
    SQLVariantRefExpr_ptr right = std::dynamic_pointer_cast<SQLVariantRefExpr>(x->getRight());
    int index = right->getIndex();
    if (index >= 0 && index < inputParameters->size())
    {
      //     Object param = inputParameters->get(index);
      //     if (param instanceof Collection) {
      //         x->getLeft()->accept(SharedObject(SQLASTOutputVisitor));
      //         print0(" IN (");
      //         right->accept(SharedObject(SQLASTOutputVisitor));
      //         print(')');
      //         return BOOL::FALSE;
      //     }
    }
  }

  SQLObject_ptr parent = x->getParent();
  bool isRoot = instanceof <SQLObject, SQLSelectQueryBlock>(parent);
  bool relational = operator_->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str() ||
                    operator_->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str();

  if (isRoot && relational)
  {
    this->indentCount++;
  }

  SQLExpr_list_ptr groupList = std::make_shared<std::list<SQLExpr_ptr>>();
  SQLExpr_ptr left = x->getLeft();
  SQLExpr_ptr right = x->getRight();

  if (inputParameters != nullptr &&
      operator_->name->c_str() != SQLBinaryOperator::Equality.name->c_str())
  {
    int varIndex = -1;
    if (instanceof <SQLExpr, SQLVariantRefExpr>(right))
    {
      varIndex = (std::dynamic_pointer_cast<SQLVariantRefExpr>(right))->getIndex();
    }

    Object_ptr param = nullptr;
    if (varIndex >= 0 && varIndex < inputParameters->size())
    {
      auto it_inputParameters = inputParameters->begin();
      std::advance(it_inputParameters, varIndex);
      param = *it_inputParameters;
    }

    // if (param instanceof Collection) {
    //     Collection values = (Collection) param;

    //     if (values->size() > 0) {
    //         print('(');
    //         int valIndex = 0;
    //         for (Object value : values) {
    //             if (valIndex++ != 0) {
    //                 print0(ucase ? " OR " : " or ");
    //             }
    //             printExpr(left, parameterized);
    //             print(' ');
    //             if (operator_ == SQLBinaryOperator::Is) {
    //                 print('=');
    //             } else {
    //                 printOperator(operator_);
    //             }
    //             print(' ');
    //             printParameter(value);
    //         }
    //         print(')');
    //         return BOOL::FALSE;
    //     }
    // }
  }

  if (operator_->isRelational() &&
      instanceof <SQLExpr, SQLIntegerExpr>(left) &&
      instanceof <SQLExpr, SQLIntegerExpr>(right))
  {
    print((std::dynamic_pointer_cast<SQLIntegerExpr>(left))->getNumber());
    print(' ');
    printOperator(operator_);
    print(' ');
    double number = (std::dynamic_pointer_cast<SQLIntegerExpr>(right))->getNumber();
    // if (number instanceof BigInteger) {
    //     print0(((BigInteger) number).toString());
    // } else {
    print(number);
    // }
    return false;
  }

  for (;;)
  {
    if (instanceof <SQLExpr, SQLBinaryOpExpr>(left) &&
                       (std::dynamic_pointer_cast<SQLBinaryOpExpr>(left))->getOperator()->name->c_str() == operator_->name->c_str() &&
                       operator_->name->c_str() != SQLBinaryOperator::IsNot.name->c_str() &&
                       operator_->name->c_str() != SQLBinaryOperator::Is.name->c_str())
    {
      SQLBinaryOpExpr_ptr binaryLeft = std::dynamic_pointer_cast<SQLBinaryOpExpr>(left);
      groupList->push_back(binaryLeft->getRight());
      left = binaryLeft->getLeft();
    }
    else
    {
      groupList->push_back(left);
      break;
    }
  }

  auto it_groupList = groupList->begin();
  for (int i = groupList->size() - 1; i >= 0; --i)
  {
    std::advance(it_groupList, i);
    SQLExpr_ptr item = *it_groupList;

    if (relational)
    {
      if (isPrettyFormat() && item->hasBeforeComment() && !parameterized)
      {
        printlnComments(item->getBeforeCommentsDirect());
      }
    }

    if (isPrettyFormat() && item->hasBeforeComment() && !parameterized)
    {
      printlnComments(item->getBeforeCommentsDirect());
    }

    visitBinaryLeft(item, operator_);

    if (isPrettyFormat() && item->hasAfterComment())
    {
      print(' ');
      printlnComment(item->getAfterCommentsDirect());
    }

    if (i != groupList->size() - 1 &&
        isPrettyFormat() &&
        item->getParent() != nullptr &&
        item->getParent()->hasAfterComment() &&
        !parameterized)
    {
      print(' ');
      printlnComment(item->getParent()->getAfterCommentsDirect());
    }

    BOOL_ptr printOpSpace = BOOL::TRUE;
    if (relational)
    {
      println();
    }
    else
    {
      if (operator_->name->c_str() == SQLBinaryOperator::Modulus.name->c_str() &&
              DbType::oracle.name->c_str() == dbType->name->c_str() &&
          instanceof <SQLExpr, SQLIdentifierExpr>(left) &&
          instanceof <SQLExpr, SQLIdentifierExpr>(right) && StringUtils::equalsIgnoreCase((std::dynamic_pointer_cast<SQLIdentifierExpr>(right))->getName(), make_string_ptr("NOTFOUND")))
      {
        printOpSpace = BOOL::FALSE;
      }
      if (printOpSpace)
      {
        print(' ');
      }
    }
    printOperator(operator_);
    if (printOpSpace)
    {
      print(' ');
    }
  }

  visitorBinaryRight(x);

  if (isRoot && relational)
  {
    this->indentCount--;
  }

  return false;
}

void SQLASTOutputVisitor::printOperator(SQLBinaryOperator_ptr operator_)
{
  print0(ucase ? operator_->name : operator_->nameLCase);
}

void SQLASTOutputVisitor::visitorBinaryRight(SQLBinaryOpExpr_ptr x)
{
  SQLExpr_ptr right = x->getRight();
  SQLBinaryOperator_ptr op = x->getOperator();
  if (isPrettyFormat() && right->hasBeforeComment())
  {
    printlnComments(right->getBeforeCommentsDirect());
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(right))
  {
    SQLBinaryOpExpr_ptr binaryRight = std::dynamic_pointer_cast<SQLBinaryOpExpr>(right);
    SQLBinaryOperator_ptr rightOp = binaryRight->getOperator();

    bool rightRational = rightOp->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str() || rightOp->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str();

    if (rightOp->priority >= op->priority ||
        (binaryRight->isParenthesized() &&
         rightOp->name->c_str() != op->name->c_str() &&
         rightOp->isLogical() &&
         op->isLogical()))
    {
      if (rightRational)
      {
        this->indentCount++;
      }

      print('(');
      printExpr(binaryRight, parameterized);
      print(')');

      if (rightRational)
      {
        this->indentCount--;
      }
    }
    else
    {
      printExpr(binaryRight, parameterized);
    }
  }
  else if (instanceof <SQLExpr, SQLBinaryOpExprGroup>(right))
  {
    SQLBinaryOpExprGroup_ptr group = std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(right);
    if (group->getOperator()->name->c_str() == x->getOperator()->name->c_str())
    {
      visit(group);
    }
    else
    {
      indentCount++;
      print('(');
      visit(group);
      print(')');
      indentCount--;
    }
  }
  else if (SQLBinaryOperator::Equality.priority >= op->priority &&
           (instanceof <SQLExpr, SQLInListExpr>(right) ||
            instanceof <SQLExpr, SQLBetweenExpr>(right) ||
            instanceof <SQLExpr, SQLNotExpr>(right)))
  {
    indentCount++;
    print('(');
    printExpr(right, parameterized);
    print(')');
    indentCount--;
  }
  else
  {
    printExpr(right, parameterized);
  }

  if (right->hasAfterComment() && isPrettyFormat())
  {
    print(' ');
    printlnComment(right->getAfterCommentsDirect());
  }

  if (x->getHint() != nullptr)
  {
    x->getHint()->accept(SharedObject(SQLASTOutputVisitor));
  }
}

void SQLASTOutputVisitor::visitBinaryLeft(SQLExpr_ptr left, SQLBinaryOperator_ptr op)
{
  if (instanceof <SQLExpr, SQLBinaryOpExpr>(left))
  {
    SQLBinaryOpExpr_ptr binaryLeft = std::dynamic_pointer_cast<SQLBinaryOpExpr>(left);
    SQLBinaryOperator_ptr leftOp = binaryLeft->getOperator();
    SQLObject_ptr parent = left->getParent();
    bool leftRational = leftOp->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str() ||
                        leftOp->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str();

    BOOL_ptr bracket;
    if (leftOp->priority > op->priority)
    {
      bracket = BOOL::TRUE;
    }
    else if (leftOp->priority == op->priority &&
             instanceof <SQLObject, SQLBinaryOpExpr>(parent) &&
             instanceof <SQLObject, SQLBinaryOpExpr>(parent->getParent()) && leftOp->priority == (std::dynamic_pointer_cast<SQLBinaryOpExpr>(parent->getParent()))->getOperator()->priority && left == (std::dynamic_pointer_cast<SQLBinaryOpExpr>(parent))->getRight())
    {
      bracket = BOOL::TRUE;
    }
    else if ((leftOp->name->c_str() == SQLBinaryOperator::Is.name->c_str() ||
              leftOp->name->c_str() == SQLBinaryOperator::IsNot.name->c_str()) &&
             !op->isLogical())
    {
      bracket = BOOL::TRUE;
    }
    else if (binaryLeft->isParenthesized())
    {
      if (leftOp != op &&
          ((leftOp->isLogical() &&
            op->isLogical()) ||
           op->name->c_str() == SQLBinaryOperator::Is.name->c_str()))
      {
        bracket = BOOL::TRUE;
      }
      else
      {
        bracket = BOOL::FALSE;
      }
    }
    else
    {
      bracket = BOOL::FALSE;
    }

    if (bracket)
    {
      if (leftRational)
      {
        this->indentCount++;
      }
      print('(');
      printExpr(left, parameterized);
      print(')');

      if (leftRational)
      {
        this->indentCount--;
      }
    }
    else
    {
      printExpr(left, parameterized);
    }
  }
  else if (instanceof <SQLExpr, SQLBinaryOpExprGroup>(left))
  {
    SQLBinaryOpExprGroup_ptr group = std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(left);
    if (group->getOperator()->name->c_str() == op->name->c_str())
    {
      visit(group);
    }
    else
    {
      indentCount++;
      print('(');
      visit(group);
      print(')');
      indentCount--;
    }
  }
  else if (instanceof <SQLExpr, SQLInListExpr>(left))
  {
    SQLInListExpr_ptr inListExpr = std::dynamic_pointer_cast<SQLInListExpr>(left);
    bool quote;
    if (inListExpr->isNot())
    {
      quote = op->priority <= SQLBinaryOperator::Equality.priority;
    }
    else
    {
      quote = op->priority < SQLBinaryOperator::Equality.priority;
    }
    if (quote)
    {
      print('(');
    }
    visit(inListExpr);
    if (quote)
    {
      print(')');
    }
  }
  else if (instanceof <SQLExpr, SQLBetweenExpr>(left))
  {
    SQLBetweenExpr_ptr betweenExpr = std::dynamic_pointer_cast<SQLBetweenExpr>(left);
    bool quote;
    if (betweenExpr->isNot())
    {
      quote = op->priority <= SQLBinaryOperator::Equality.priority;
    }
    else
    {
      quote = op->priority < SQLBinaryOperator::Equality.priority;
    }
    if (quote)
    {
      print('(');
    }
    visit(betweenExpr);
    if (quote)
    {
      print(')');
    }
  }
  else if (instanceof <SQLExpr, SQLNotExpr>(left))
  {
    print('(');
    printExpr(left);
    print(')');
  }
  else if (instanceof <SQLExpr, SQLUnaryExpr>(left))
  {
    SQLUnaryExpr_ptr unary = std::dynamic_pointer_cast<SQLUnaryExpr>(left);

    bool quote = true;
    if (unary->getOperator()->name->c_str() == SQLUnaryOperator::BINARY.name->c_str())
    {
      quote = false;
    }
    else if (unary->getOperator()->name->c_str() == SQLUnaryOperator::Plus.name->c_str() ||
             unary->getOperator()->name->c_str() == SQLUnaryOperator::Negative.name->c_str())
    {
      quote = op->priority < SQLBinaryOperator::Add.priority;
    }

    if (quote)
    {
      print('(');
      printExpr(left);
      print(')');
    }
    else
    {
      printExpr(left);
    }
  }
  else
  {
    printExpr(left, parameterized);
  }
}

void SQLASTOutputVisitor::printTableSource(SQLTableSource_ptr x)
{
  string_ptr clazz = x->getClass();
  if (clazz->c_str() == SQLJoinTableSource::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLJoinTableSource>(x));
  }
  else if (clazz->c_str() == SQLExprTableSource::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLExprTableSource>(x));
  }
  else if (clazz->c_str() == SQLSubqueryTableSource::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLSubqueryTableSource>(x));
  }
  else
  {
    x->accept(SharedObject(SQLASTOutputVisitor));
  }
}

void SQLASTOutputVisitor::printQuery(SQLSelectQuery_ptr x)
{
  string_ptr clazz = x->getClass();
  if (clazz->c_str() == SQLSelectQueryBlock::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLSelectQueryBlock>(x));
  }
  else if (clazz->c_str() == SQLUnionQuery::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLUnionQuery>(x));
  }
  else
  {
    x->accept(SharedObject(SQLASTOutputVisitor));
  }
}

void SQLASTOutputVisitor::printExpr(SQLExpr_ptr x)
{
  printExpr(x, parameterized);
}

void SQLASTOutputVisitor::printExpr(SQLExpr_ptr x, BOOL_ptr parameterized)
{
  string_ptr clazz = x->getClass();
  if (clazz->c_str() == SQLIdentifierExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLIdentifierExpr>(x));
  }
  else if (clazz->c_str() == SQLPropertyExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLPropertyExpr>(x));
  }
  else if (clazz->c_str() == SQLAllColumnExpr::class_->c_str())
  {
    print('*');
  }
  else if (clazz->c_str() == SQLAggregateExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLAggregateExpr>(x));
  }
  else if (clazz->c_str() == SQLBinaryOpExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLBinaryOpExpr>(x));
  }
  else if (clazz->c_str() == SQLCharExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLCharExpr>(x), parameterized);
  }
  else if (clazz->c_str() == SQLNullExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLNullExpr>(x));
  }
  else if (clazz->c_str() == SQLIntegerExpr::class_->c_str())
  {
    printInteger(std::dynamic_pointer_cast<SQLIntegerExpr>(x), parameterized);
  }
  else if (clazz->c_str() == SQLNumberExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLNumberExpr>(x));
  }
  else if (clazz->c_str() == SQLMethodInvokeExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLMethodInvokeExpr>(x));
  }
  else if (clazz->c_str() == SQLVariantRefExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLVariantRefExpr>(x));
  }
  else if (clazz->c_str() == SQLBinaryOpExprGroup::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(x));
  }
  else if (clazz->c_str() == SQLCaseExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLCaseExpr>(x));
  }
  else if (clazz->c_str() == SQLInListExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLInListExpr>(x));
  }
  else if (clazz->c_str() == SQLNotExpr::class_->c_str())
  {
    visit(std::dynamic_pointer_cast<SQLNotExpr>(x));
  }
  else
  {
    x->accept(SharedObject(SQLASTOutputVisitor));
  }
}

bool SQLASTOutputVisitor::visit(SQLCaseExpr_ptr x)
{
  this->indentCount++;
  print0(ucase ? make_string_ptr("CASE ") : make_string_ptr("case "));

  SQLExpr_ptr valueExpr = x->getValueExpr();
  if (valueExpr != nullptr)
  {
    printExpr(valueExpr, parameterized);
  }

  SQLCaseExpr_Item_list_ptr items = x->getItems();
  auto it_items = items->begin();
  for (int i = 0, size = items->size(); i < size; ++i)
  {
    println();
    std::advance(it_items, i);
    visit(*it_items);
  }

  SQLExpr_ptr elExpr = x->getElseExpr();
  if (elExpr != nullptr)
  {
    println();
    print0(ucase ? make_string_ptr("ELSE ") : make_string_ptr("else "));
    if (instanceof <SQLExpr, SQLCaseExpr>(elExpr))
    {
      this->indentCount++;
      println();
      visit(std::dynamic_pointer_cast<SQLCaseExpr>(elExpr));
      this->indentCount--;
    }
    else
    {
      printExpr(elExpr, parameterized);
    }
  }

  this->indentCount--;
  println();
  print0(ucase ? make_string_ptr("END") : make_string_ptr("end"));

  return false;
}

bool SQLASTOutputVisitor::visit(SQLCaseExpr_Item_ptr x)
{
  print0(ucase ? make_string_ptr("WHEN ") : make_string_ptr("when "));
  SQLExpr_ptr conditionExpr = x->getConditionExpr();
  int lines = this->lines;
  this->indentCount++;
  printExpr(conditionExpr, parameterized);
  this->indentCount--;

  bool println_ = lines != this->lines;
  if (isPrettyFormat() && !println_)
  {
    string_list_ptr afterComments = conditionExpr->getAfterCommentsDirect();
    if (afterComments != nullptr && !afterComments->empty())
    {
      println_ = true;
    }
  }

  if (println_)
  {
    println();
  }
  else
  {
    print(' ');
  }

  print0(ucase ? make_string_ptr("THEN ") : make_string_ptr("then "));
  SQLExpr_ptr valueExpr = x->getValueExpr();
  if (instanceof <SQLExpr, SQLCaseExpr>(valueExpr))
  {
    this->indentCount++;
    println();
    visit(std::dynamic_pointer_cast<SQLCaseExpr>(valueExpr));
    this->indentCount--;
  }
  else
  {
    this->indentCount++;
    printExpr(valueExpr, parameterized);
    this->indentCount--;
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLCaseStatement_ptr x)
{
  print0(ucase ? make_string_ptr("CASE") : make_string_ptr("case"));
  SQLExpr_ptr valueExpr = x->getValueExpr();
  if (valueExpr != nullptr)
  {
    print(' ');
    printExpr(valueExpr, parameterized);
  }
  this->indentCount++;
  println();
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLCaseStatement_Item, SQLObject>(x->getItems());
  printlnAndAccept(tmp1, make_string_ptr(" "));

  if (x->getElseStatements()->size() > 0)
  {
    println();
    print0(ucase ? make_string_ptr("ELSE ") : make_string_ptr("else "));
    SQLObject_list_ptr tmp2 = ListTran::tran_class<SQLStatement, SQLObject>(x->getElseStatements());
    printlnAndAccept(tmp2, make_string_ptr(""));
  }

  this->indentCount--;

  println();
  print0(ucase ? make_string_ptr("END CASE") : make_string_ptr("end case"));
  if (DbType::oracle.name->c_str() == dbType->name->c_str())
  {
    print(';');
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLCaseStatement_Item_ptr x)
{
  print0(ucase ? make_string_ptr("WHEN ") : make_string_ptr("when "));
  printExpr(x->getConditionExpr(), parameterized);
  print0(ucase ? make_string_ptr(" THEN ") : make_string_ptr(" then "));

  SQLStatement_ptr stmt = x->getStatement();
  if (stmt != nullptr)
  {
    stmt->accept(SharedObject(SQLASTOutputVisitor));
    print(';');
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLCastExpr_ptr x)
{
  if (x->isTry())
  {
    print0(ucase ? make_string_ptr("TRY_CAST(") : make_string_ptr("try_cast("));
  }
  else
  {
    print0(ucase ? make_string_ptr("CAST(") : make_string_ptr("cast("));
  }
  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? make_string_ptr(" AS ") : make_string_ptr(" as "));
  x->getDataType()->accept(SharedObject(SQLASTOutputVisitor));
  print0(make_string_ptr(")"));

  return false;
}

bool SQLASTOutputVisitor::visit(SQLCharExpr_ptr x)
{
  return visit(x, this->parameterized);
}

bool SQLASTOutputVisitor::visit(SQLCharExpr_ptr x, BOOL_ptr parameterized)
{
  if (parameterized)
  {
    print('?');
    incrementReplaceCunt();
    if (this->parameters != nullptr)
    {
      ExportParameterVisitorUtils::exportParameter(this->parameters, x);
    }
    return false;
  }

  printChars(x->getText());

  return false;
}

void SQLASTOutputVisitor::printChars(string_ptr text)
{
  if (text == nullptr)
  {
    print0(ucase ? make_string_ptr("NULL") : make_string_ptr("nullptr"));
  }
  else
  {
    print('\'');
    int index = text->find('\'');
    if (index != std::string::npos)
    {
      text = StringUtils::replaceAll(text, make_string_ptr("'"), make_string_ptr("''"));
    }
    print0(text);
    print('\'');
  }
}

bool SQLASTOutputVisitor::visit(SQLDataType_ptr x)
{
  printDataType(x);

  return false;
}

void SQLASTOutputVisitor::printDataType(SQLDataType_ptr x)
{
  BOOL_ptr parameterized = this->parameterized;
  this->parameterized = BOOL::FALSE;

  print0(x->getName());
  SQLExpr_list_ptr arguments = x->getArguments();
  if (arguments->size() > 0)
  {
    print('(');
    auto it_arguments = arguments->begin();
    for (int i = 0, size = arguments->size(); i < size; ++i)
    {
      if (i != 0)
      {
        print0(", ");
      }
      std::advance(it_arguments, i);
      printExpr(*it_arguments, BOOL::FALSE);
    }
    print(')');
  }

  BOOL_ptr withTimeZone = x->getWithTimeZone();
  // if (withTimeZone != nullptr) {
  if (withTimeZone)
  {
    if (x->isWithLocalTimeZone())
    {
      print0(ucase ? make_string_ptr(" WITH LOCAL TIME ZONE") : make_string_ptr(" with local time zone"));
    }
    else
    {
      print0(ucase ? make_string_ptr(" WITH TIME ZONE") : make_string_ptr(" with time zone"));
    }
  }
  else
  {
    print0(ucase ? make_string_ptr(" WITHOUT TIME ZONE") : make_string_ptr(" without time zone"));
  }
  // }

  if (instanceof <SQLDataType, SQLDataTypeImpl>(x))
  {
    SQLExpr_ptr indexBy = (std::dynamic_pointer_cast<SQLDataTypeImpl>(x))->getIndexBy();
    if (indexBy != nullptr)
    {
      print0(ucase ? make_string_ptr(" INDEX BY ") : make_string_ptr(" index by "));
      indexBy->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  this->parameterized = parameterized;
}

bool SQLASTOutputVisitor::visit(SQLCharacterDataType_ptr x)
{
  visit(std::dynamic_pointer_cast<SQLDataType>(x));

  SQLCommentHint_list_ptr hints = (std::dynamic_pointer_cast<SQLCharacterDataType>(x))->hints;
  if (hints != nullptr)
  {
    print(' ');
    for (SQLCommentHint_ptr hint : *hints)
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLExistsExpr_ptr x)
{
  if (x->isNot())
  {
    print0(ucase ? make_string_ptr("NOT EXISTS (") : make_string_ptr("not exists ("));
  }
  else
  {
    print0(ucase ? make_string_ptr("EXISTS (") : make_string_ptr("exists ("));
  }
  this->indentCount++;
  println();
  visit(x->getSubQuery());
  this->indentCount--;
  println();
  print(')');

  SQLCommentHint_ptr hint = x->getHint();
  if (hint != nullptr)
  {
    print(' ');
    hint->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLIdentifierExpr_ptr x)
{
  printName0(x->getName());
  return false;
}

bool SQLASTOutputVisitor::printName(SQLName_ptr x, string_ptr name)
{
  bool shardingSupport = this->shardingSupport && this->parameterized;
  return printName(x, name, shardingSupport);
}

string_ptr SQLASTOutputVisitor::unwrapShardingTable(string_ptr name)
{
  char c0 = name->at(0);
  char c_last = name->at(name->length() - 1);
  bool quote = (c0 == '`' && c_last == '`') || (c0 == '"' && c_last == '"');

  int end = name->length();
  if (quote)
  {
    end--;
  }

  int num_cnt = 0, postfixed_cnt = 0;
  for (int i = end - 1; i > 0; --i, postfixed_cnt++)
  {
    char ch = name->at(i);
    if (ch >= '0' && ch <= '9')
    {
      num_cnt++;
    }

    if (ch != '_' && (ch < '0' || ch > '9'))
    {
      break;
    }
  }
  if (num_cnt < 1 || postfixed_cnt < 2)
  {
    return name;
  }

  int start = end - postfixed_cnt;
  if (start < 1)
  {
    return name;
  }

  string_ptr realName = make_string_ptr(name->substr(quote ? 1 : 0, start));
  return realName;
}

bool SQLASTOutputVisitor::printName(SQLName_ptr x, string_ptr name, bool shardingSupport)
{
  if (shardingSupport)
  {
    SQLObject_ptr parent = x->getParent();

    if (instanceof <SQLObject, SQLExprTableSource>(parent) ||
        instanceof <SQLObject, SQLPropertyExpr>(parent))
    {
      shardingSupport = true;
    }
    else
    {
      shardingSupport = false;
    }

    if (instanceof <SQLObject, SQLPropertyExpr>(parent) &&
        instanceof <SQLObject, SQLExprTableSource>(parent->getParent()))
    {
      shardingSupport = false;
    }
  }

  if (shardingSupport)
  {
    bool quote = name->at(0) == '`' && name->at(name->length() - 1) == '`';

    string_ptr unwrappedName = unwrapShardingTable(name);
    if (unwrappedName != name)
    {
      bool isAlias = false;
      for (SQLObject_ptr parent = x->getParent(); parent != nullptr; parent = parent->getParent())
      {
        if (instanceof <SQLObject, SQLSelectQueryBlock>(parent))
        {
          SQLTableSource_ptr from = (std::dynamic_pointer_cast<SQLSelectQueryBlock>(parent))->getFrom();
          if (quote)
          {
            string_ptr name2 = make_string_ptr(name->substr(1, name->length() - 1));
            string_list_ptr tmp = std::make_shared<std::list<string_ptr>>();
            tmp->push_back(name);
            tmp->push_back(name2);
            if (isTableSourceAlias(from, tmp))
            {
              isAlias = true;
            }
          }
          else
          {
            if (isTableSourceAlias(from, name))
            {
              isAlias = true;
            }
          }
          break;
        }
      }

      if (!isAlias)
      {
        print0(unwrappedName);
        incrementReplaceCunt();
        return false;
      }
      else
      {
        printName0(name);
        return false;
      }
    }
  }

  printName0(name);
  return false;
}

bool SQLASTOutputVisitor::visit(SQLInListExpr_ptr x)
{
  SQLExpr_ptr expr = x->getExpr();

  bool quote = false;
  if (instanceof <SQLExpr, SQLBinaryOpExpr>(expr))
  {
    SQLBinaryOperator_ptr operator_ = (std::dynamic_pointer_cast<SQLBinaryOpExpr>(expr))->getOperator();
    if (operator_->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str() ||
        operator_->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str() ||
        operator_->name->c_str() == SQLBinaryOperator::BooleanXor.name->c_str() ||
        operator_->name->c_str() == SQLBinaryOperator::Assignment.name->c_str())
    {
      quote = true;
    }
    else
    {
      quote = (*(std::dynamic_pointer_cast<SQLBinaryOpExpr>(expr))->isParenthesized()) == BOOL::TRUE;
    }
  }
  else if (instanceof <SQLExpr, SQLNotExpr>(expr) ||
           instanceof <SQLExpr, SQLBetweenExpr>(expr) ||
           instanceof <SQLExpr, SQLInListExpr>(expr) ||
           instanceof <SQLExpr, SQLUnaryExpr>(expr) ||
           instanceof <SQLExpr, SQLBinaryOpExprGroup>(expr))
  {
    quote = true;
  }

  if (this->parameterized)
  {
    SQLExpr_list_ptr targetList = x->getTargetList();

    BOOL_ptr allLiteral = BOOL::TRUE;
    for (SQLExpr_ptr item : *targetList)
    {
      if (!(instanceof <SQLExpr, SQLLiteralExpr>(item) ||
            instanceof <SQLExpr, SQLVariantRefExpr>(item)))
      {
        if (instanceof <SQLExpr, SQLListExpr>(item))
        {
          SQLListExpr_ptr list = std::dynamic_pointer_cast<SQLListExpr>(item);
          for (SQLExpr_ptr listItem : *list->getItems())
          {
            if (!(instanceof <SQLExpr, SQLLiteralExpr>(listItem) ||
                  instanceof <SQLExpr, SQLVariantRefExpr>(listItem)))
            {
              allLiteral = BOOL::FALSE;
              break;
            }
          }
          if (allLiteral)
          {
            break;
          }
          continue;
        }
        allLiteral = BOOL::FALSE;
        break;
      }
    }

    if (allLiteral)
    {
      BOOL_ptr changed = BOOL::TRUE;
      if (targetList->size() == 1)
      {
        auto it_targetList = targetList->begin();
        std::advance(it_targetList, 0);
        if (instanceof <SQLExpr, SQLVariantRefExpr>(*it_targetList))
        {
          changed = BOOL::FALSE;
        }
      }

      if (quote)
      {
        print('(');
      }
      printExpr(expr, parameterized);
      if (quote)
      {
        print(')');
      }

      if (x->isNot())
      {
        print(ucase ? make_string_ptr(" NOT IN") : make_string_ptr(" not in"));
      }
      else
      {
        print(ucase ? make_string_ptr(" IN") : make_string_ptr(" in"));
      }

      if ((!parameterizedQuesUnMergeInList) ||
          (targetList->size() == 1))
      {
        auto it_targetList = targetList->begin();
        std::advance(it_targetList, 0);
        if (!(instanceof <SQLExpr, SQLListExpr>(*it_targetList)))
        {
          print(make_string_ptr(" (?)"));
        }
      }
      else
      {
        print(make_string_ptr(" ("));
        auto it_targetList = targetList->begin();
        for (int i = 0; i < targetList->size(); i++)
        {
          if (i != 0)
          {
            print(make_string_ptr(", "));
          }
          std::advance(it_targetList, i);
          SQLExpr_ptr item = *it_targetList;
          if (instanceof <SQLExpr, SQLListExpr>(item))
          {
            visit(std::dynamic_pointer_cast<SQLListExpr>(item));
            changed = BOOL::FALSE;
          }
          else
          {
            print(make_string_ptr("?"));
          }
        }
        print(make_string_ptr(")"));
      }

      if (changed)
      {
        incrementReplaceCunt();
        if (this->parameters != nullptr)
        {
          if (parameterizedMergeInList)
          {
            Object_list_ptr subList = std::make_shared<std::list<Object_ptr>>();
            for (SQLExpr_ptr target : *x->getTargetList())
            {
              ExportParameterVisitorUtils::exportParameter(subList, target);
            }
            if (subList != nullptr)
            {
              // parameters->push_back(subList);
              for (auto it : *subList)
              {
                parameters->push_back(it);
              }
            }
          }
          else
          {
            for (SQLExpr_ptr target : *x->getTargetList())
            {
              ExportParameterVisitorUtils::exportParameter(this->parameters, target);
            }
          }
        }
      }

      if (x->getHint() != nullptr)
      {
        x->getHint()->accept(SharedObject(SQLASTOutputVisitor));
      }

      return false;
    }
  }

  if (quote)
  {
    print('(');
  }
  printExpr(expr, parameterized);
  if (quote)
  {
    print(')');
  }

  if (x->isNot())
  {
    print0(ucase ? make_string_ptr(" NOT IN (") : make_string_ptr(" not in ("));
  }
  else
  {
    print0(ucase ? make_string_ptr(" IN (") : make_string_ptr(" in ("));
  }

  SQLExpr_list_ptr list = x->getTargetList();

  bool printLn = false;
  if (list->size() > 5)
  {
    printLn = true;
    auto it_list = list->begin();
    for (int i = 0, size = list->size(); i < size; ++i)
    {
      std::advance(it_list, i);
      if (!(instanceof <SQLExpr, SQLCharExpr>(*it_list)))
      {
        printLn = false;
        break;
      }
    }
  }

  if (printLn)
  {
    this->indentCount++;
    println();
    auto it_list = list->begin();
    for (int i = 0, size = list->size(); i < size; ++i)
    {
      if (i != 0)
      {
        print0(make_string_ptr(", "));
        println();
      }
      std::advance(it_list, i);
      SQLExpr_ptr item = *it_list;
      printExpr(item, parameterized);
    }
    this->indentCount--;
    println();
  }
  else
  {
    SQLExpr_list_ptr targetList = x->getTargetList();
    auto it_targetList = targetList->begin();
    for (int i = 0; i < targetList->size(); i++)
    {
      if (i != 0)
      {
        print0(make_string_ptr(", "));
      }
      std::advance(it_targetList, i);
      printExpr(*it_targetList, parameterized);
    }
  }

  print(')');

  string_list_ptr afterComments = x->getAfterCommentsDirect();
  if (afterComments != nullptr && !afterComments->empty())
  {
    auto it_afterComments = afterComments->begin();
    std::advance(it_afterComments, 0);
    if (StringUtils::startWith(*it_afterComments, make_string_ptr("--")))
    {
      print(' ');
    }
  }
  printlnComment(afterComments);

  if (x->getHint() != nullptr)
  {
    x->getHint()->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLContainsExpr_ptr x)
{
  SQLExpr_ptr expr = x->getExpr();
  if (expr != nullptr)
  {
    printExpr(expr, parameterized);
    print(' ');
  }

  if (x->isNot())
  {
    print0(ucase ? make_string_ptr("NOT CONTAINS (") : make_string_ptr(" not contains ("));
  }
  else
  {
    print0(ucase ? make_string_ptr("CONTAINS (") : make_string_ptr(" contains ("));
  }

  SQLExpr_list_ptr list = x->getTargetList();

  bool printLn = false;
  if (list->size() > 5)
  {
    printLn = true;
    auto it_list = list->begin();
    for (int i = 0, size = list->size(); i < size; ++i)
    {
      std::advance(it_list, i);
      if (!(instanceof <SQLExpr, SQLCharExpr>(*it_list)))
      {
        printLn = false;
        break;
      }
    }
  }

  if (printLn)
  {
    this->indentCount++;
    println();
    auto it_list = list->begin();
    for (int i = 0, size = list->size(); i < size; ++i)
    {
      if (i != 0)
      {
        print0(make_string_ptr(", "));
        println();
      }
      std::advance(it_list, i);
      SQLExpr_ptr item = *it_list;
      printExpr(item, parameterized);
    }
    this->indentCount--;
    println();
  }
  else
  {
    SQLExpr_list_ptr targetList = x->getTargetList();
    auto it_targetList = targetList->begin();
    for (int i = 0; i < targetList->size(); i++)
    {
      if (i != 0)
      {
        print0(make_string_ptr(", "));
      }
      printExpr(*it_targetList, parameterized);
    }
  }

  print(')');
  return false;
}

bool SQLASTOutputVisitor::visit(SQLIntegerExpr_ptr x)
{
  BOOL_ptr parameterized = this->parameterized;
  printInteger(x, parameterized);
  return false;
}

void SQLASTOutputVisitor::printInteger(SQLIntegerExpr_ptr x, BOOL_ptr parameterized)
{
  double number = x->getNumber();

  if (doubleEqual::AreEqual(number, ONE))
  {
    if (DbType::oracle.name->c_str() == dbType->name->c_str())
    {
      SQLObject_ptr parent = x->getParent();
      if (instanceof <SQLObject, SQLBinaryOpExpr>(parent))
      {
        SQLBinaryOpExpr_ptr binaryOpExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(parent);
        SQLExpr_ptr left = binaryOpExpr->getLeft();
        SQLBinaryOperator_ptr op = binaryOpExpr->getOperator();
        if (instanceof <SQLExpr, SQLIdentifierExpr>(left) &&
                           op->name->c_str() == SQLBinaryOperator::Equality.name->c_str())
        {
          string_ptr name = (std::dynamic_pointer_cast<SQLIdentifierExpr>(left))->getName();
          if ("rownum" == name->c_str())
          {
            print(1);
            return;
          }
        }
      }
    }
  }

  if (parameterized)
  {
    print('?');
    incrementReplaceCunt();

    if (this->parameters != nullptr)
    {
      ExportParameterVisitorUtils::exportParameter(this->parameters, x);
    }
    return;
  }

  // if (number instanceof BigDecimal || number instanceof BigInteger) {
  //     print(number.toString());
  // } else {
  //     print(number.longValue());
  // }
  print(number);
}

bool SQLASTOutputVisitor::visit(SQLMethodInvokeExpr_ptr x)
{
  SQLExpr_ptr owner = x->getOwner();
  if (owner != nullptr)
  {
    printMethodOwner(owner);
  }

  if (parameterized)
  {
    SQLExpr_list_ptr arguments = x->getArguments();
    if (x->methodNameHashCode64() == FnvHash::Constants::TRIM && arguments->size() == 1)
    {
      auto it_arguments = arguments->begin();
      std::advance(it_arguments, 0);
      if (instanceof <SQLExpr, SQLCharExpr>(*it_arguments) &&
                         x->getTrimOption() == nullptr &&
                         x->getFrom() == nullptr)
      {
        print('?');

        if (parameters != nullptr)
        {
          SQLCharExpr_ptr charExpr = std::dynamic_pointer_cast<SQLCharExpr>(*it_arguments);
          Object_ptr value = Object_ptr(new Object());
          std::string tmp = StringUtils::Trim(charExpr->getText())->c_str();
          value->any_data_ = tmp;
          parameters->push_back(value);
        }

        replaceCount++;
        return false;
      }
    }
  }

  string_ptr function = x->getMethodName();
  if (function != nullptr)
  {
    printFunctionName(function);
  }
  printMethodParameters(x);
  return false;
}

void SQLASTOutputVisitor::printMethodParameters(SQLMethodInvokeExpr_ptr x)
{
  string_ptr function = x->getMethodName();
  SQLExpr_list_ptr parameters = x->getArguments();

  print('(');

  string_ptr trimOption = x->getTrimOption();
  if (trimOption != nullptr)
  {
    print0(trimOption);

    if (parameters->size() > 0)
    {
      print(' ');
    }
  }

  auto it_parameters = parameters->begin();
  for (int i = 0, size = parameters->size(); i < size; ++i)
  {
    if (i != 0)
    {
      print0(make_string_ptr(", "));
    }
    std::advance(it_parameters, i);
    SQLExpr_ptr param = *it_parameters;

    if (this->parameterized)
    {
      if (size == 2 && i == 1 && instanceof <SQLExpr, SQLCharExpr>(param))
      {
        if (DbType::oracle.name->c_str() == dbType->name->c_str())
        {
          if (StringUtils::equalsIgnoreCase(make_string_ptr("TO_CHAR"), function) ||
              StringUtils::equalsIgnoreCase(make_string_ptr("TO_DATE"), function))
          {
            printChars((std::dynamic_pointer_cast<SQLCharExpr>(param))->getText());
            continue;
          }
        }
        else if (DbType::mysql.name->c_str() == dbType->name->c_str())
        {
          if (StringUtils::equalsIgnoreCase(make_string_ptr("DATE_FORMAT"), function))
          {
            printChars((std::dynamic_pointer_cast<SQLCharExpr>(param))->getText());
            continue;
          }
        }
      }
    }

    if (instanceof <SQLExpr, SQLBinaryOpExpr>(param))
    {
      SQLBinaryOpExpr_ptr binaryOpExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(param);
      SQLBinaryOperator_ptr op = binaryOpExpr->getOperator();
      if (op->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str() ||
          op->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str())
      {
        this->indentCount++;
        printExpr(param, parameterized);
        this->indentCount--;
        continue;
      }
    }

    printExpr(param, parameterized);
  }

  SQLExpr_ptr from = x->getFrom();
  if (from != nullptr)
  {
    print0(ucase ? make_string_ptr(" FROM ") : make_string_ptr(" from "));
    printExpr(from, parameterized);

    SQLExpr_ptr _for = x->getFor();
    if (_for != nullptr)
    {
      print0(ucase ? make_string_ptr(" FOR ") : make_string_ptr(" for "));
      printExpr(_for, parameterized);
    }
  }

  SQLExpr_ptr using_ = x->getUsing();

  bool odpsTransformUsing = false;
  if (using_ != nullptr)
  {
    odpsTransformUsing = x->methodNameHashCode64() == FnvHash::Constants::TRANSFORM;
    if (!odpsTransformUsing)
    {
      print0(ucase ? make_string_ptr(" USING ") : make_string_ptr(" using "));
      printExpr(using_, parameterized);
    }
  }

  if (x->methodNameHashCode64() == FnvHash::Constants::WEIGHT_STRING)
  {
    SQLDataType_ptr as = std::dynamic_pointer_cast<SQLDataType>(x->getAttribute("as"));
    if (as != nullptr)
    {
      print0(ucase ? make_string_ptr(" AS ") : make_string_ptr(" as "));
      as->accept(SharedObject(SQLASTOutputVisitor));
    }

    SQLSelectOrderByItem_list_ptr levels = x->getAttribute("levels")->getSQLSelectOrderByItem_list_ptr();
    if (levels != nullptr && levels->size() > 0)
    {
      print0(ucase ? make_string_ptr(" LEVEL ") : make_string_ptr(" level "));
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(levels);
      printAndAccept(tmp1, make_string_ptr(", "));
    }

    Object_ptr reverse = x->getAttribute("reverse");
    if (reverse != nullptr)
    {
      print0(ucase ? make_string_ptr(" REVERSE") : make_string_ptr(" reverse"));
    }
  }

  print(')');

  if (odpsTransformUsing)
  {
    print0(ucase ? make_string_ptr(" USING ") : make_string_ptr(" using "));
    printExpr(using_, parameterized);
  }
}

void SQLASTOutputVisitor::printMethodOwner(SQLExpr_ptr owner)
{
  printExpr(owner, parameterized);
  print('.');
}

void SQLASTOutputVisitor::printFunctionName(string_ptr name)
{
  print0(name);
}

bool SQLASTOutputVisitor::visit(SQLAggregateExpr_ptr x)
{
  BOOL_ptr parameterized = this->parameterized;
  if (x->methodNameHashCode64() == FnvHash::Constants::GROUP_CONCAT)
  {
    this->parameterized = BOOL::FALSE;
  }
  if (x->methodNameHashCode64() == FnvHash::Constants::COUNT)
  {
    SQLExpr_list_ptr arguments = x->getArguments();
    if (arguments->size() == 1)
    {
      auto it_arguments = arguments->begin();
      std::advance(it_arguments, 0);
      SQLExpr_ptr arg0 = *it_arguments;
      if (instanceof <SQLExpr, SQLLiteralExpr>(arg0))
      {
        this->parameterized = BOOL::FALSE;
      }
    }
  }

  if (x->getOwner() != nullptr)
  {
    printExpr(x->getOwner());
    print(".");
  }

  string_ptr methodName = x->getMethodName();
  print0(ucase ? methodName : StringUtils::toLower(methodName));
  print('(');

  SQLAggregateOption_ptr option = x->getOption();
  if (option != nullptr)
  {
    print0(option->name);
    print(' ');
  }

  SQLExpr_list_ptr arguments = x->getArguments();
  auto it_arguments = arguments->begin();
  for (int i = 0, size = arguments->size(); i < size; ++i)
  {
    if (i != 0)
    {
      print0(", ");
    }
    std::advance(it_arguments, i);
    printExpr(*it_arguments, BOOL::FALSE);
  }

  BOOL_ptr withGroup = x->isWithinGroup();
  if (withGroup)
  {
    print0(ucase ? make_string_ptr(") WITHIN GROUP (") : make_string_ptr(") within group ("));
  }

  visitAggreateRest(x);

  print(')');

  if (x->isIgnoreNulls())
  {
    print0(ucase ? make_string_ptr(" IGNORE NULLS") : make_string_ptr(" ignore nulls"));
  }

  SQLKeep_ptr keep = x->getKeep();
  if (keep != nullptr)
  {
    print(' ');
    visit(keep);
  }

  SQLOver_ptr over = x->getOver();
  if (over != nullptr)
  {
    print0(ucase ? make_string_ptr(" OVER ") : make_string_ptr(" over "));
    over->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLName_ptr overRef = x->getOverRef();
  if (overRef != nullptr)
  {
    print0(ucase ? make_string_ptr(" OVER ") : make_string_ptr(" over "));
    overRef->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr filter = x->getFilter();
  if (filter != nullptr)
  {
    print0(ucase ? make_string_ptr(" FILTER (WHERE ") : make_string_ptr(" filter (where "));
    printExpr(filter);
    print(')');
  }

  this->parameterized = parameterized;
  return false;
}

void SQLASTOutputVisitor::visitAggreateRest(SQLAggregateExpr_ptr x)
{
  SQLOrderBy_ptr orderBy = x->getOrderBy();
  if (orderBy != nullptr)
  {
    if (!x->isWithinGroup())
    {
      print(' ');
    }
    orderBy->accept(SharedObject(SQLASTOutputVisitor));
  }
}

bool SQLASTOutputVisitor::visit(SQLAllColumnExpr_ptr x)
{
  SQLExpr_ptr owner = x->getOwner();
  if (owner != nullptr)
  {
    printExpr(owner);
    print('.');
  }

  print('*');

  SQLExpr_list_ptr except = x->getExcept();
  if (except != nullptr)
  {
    print(ucase ? make_string_ptr(" EXCEPT(") : make_string_ptr("except("));
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(except);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  return true;
}

bool SQLASTOutputVisitor::visit(SQLNCharExpr_ptr x)
{
  if (this->parameterized)
  {
    print('?');
    incrementReplaceCunt();

    if (this->parameters != nullptr)
    {
      ExportParameterVisitorUtils::exportParameter(this->parameters, x);
    }
    return false;
  }

  if ((x->getText() == nullptr) || (x->getText()->length() == 0))
  {
    print0(ucase ? make_string_ptr("NULL") : make_string_ptr("nullptr"));
  }
  else
  {
    print0(ucase ? make_string_ptr("N'") : make_string_ptr("n'"));
    print0(StringUtils::replaceAll(x->getText(), make_string_ptr("'"), make_string_ptr("''")));
    print('\'');
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLNotExpr_ptr x)
{
  print0(ucase ? make_string_ptr("NOT ") : make_string_ptr("not "));
  SQLExpr_ptr expr = x->getExpr();

  bool needQuote = false;

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(expr))
  {
    SQLBinaryOpExpr_ptr binaryOpExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(expr);
    needQuote = binaryOpExpr->getOperator()->isLogical();
  }
  else if (instanceof <SQLExpr, SQLInListExpr>(expr) ||
           instanceof <SQLExpr, SQLNotExpr>(expr) ||
           instanceof <SQLExpr, SQLBinaryOpExprGroup>(expr))
  {
    needQuote = true;
  }

  if (needQuote)
  {
    print('(');
  }
  printExpr(expr, parameterized);

  if (needQuote)
  {
    print(')');
  }

  SQLCommentHint_ptr hint = x->getHint();
  if (hint != nullptr)
  {
    hint->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLNullExpr_ptr x)
{
  SQLObject_ptr parent = x->getParent();
  if (this->parameterized && (instanceof <SQLObject, SQLInsertStatement_ValuesClause>(parent) ||
                              instanceof <SQLObject, SQLInListExpr>(parent) || (instanceof <SQLObject, SQLBinaryOpExpr>(parent) && (std::dynamic_pointer_cast<SQLBinaryOpExpr>(parent))->getOperator()->name->c_str() == SQLBinaryOperator::Equality.name->c_str())))
  {
    print('?');
    incrementReplaceCunt();

    if (this->parameters != nullptr)
    {
      if (instanceof <SQLObject, SQLBinaryOpExpr>(parent))
      {
        ExportParameterVisitorUtils::exportParameter((this)->getParameters(), x);
      }
      else
      {
        this->getParameters()->push_back(nullptr);
      }
    }
    return false;
  }

  print0(ucase ? make_string_ptr("NULL") : make_string_ptr("nullptr"));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLNumberExpr_ptr x)
{
  if (this->parameterized)
  {
    print('?');
    incrementReplaceCunt();

    if (this->parameters != nullptr)
    {
      ExportParameterVisitorUtils::exportParameter((this)->getParameters(), x);
    }
    return false;
  }

  // if (appender instanceof StringBuilder) {
  //     x->output((StringBuilder) appender);
  // } else if (appender instanceof StringBuilder) {
  //     x->output((StringBuilder) appender);
  // } else {
  //     print0(x->getNumber().toString());
  // }
  x->output(appender);
  return false;
}

bool SQLASTOutputVisitor::visit(SQLPropertyExpr_ptr x)
{
  SQLExpr_ptr owner = x->getOwner();

  string_ptr mapTableName = nullptr, ownerName = nullptr;
  if (instanceof <SQLExpr, SQLIdentifierExpr>(owner))
  {
    ownerName = (std::dynamic_pointer_cast<SQLIdentifierExpr>(owner))->getName();
    if (tableMapping != nullptr)
    {
      auto it = tableMapping->find(ownerName->c_str());
      if (it != tableMapping->end())
      {
        mapTableName = it->second;
      }

      if (mapTableName == nullptr &&
          ownerName->length() > 2 &&
          ownerName->at(0) == '`' &&
          ownerName->at(ownerName->length() - 1) == '`')
      {
        ownerName = make_string_ptr(ownerName->substr(1, ownerName->length() - 1));
        it = tableMapping->find(ownerName->c_str());
        if (it != tableMapping->end())
        {
          mapTableName = it->second;
        }
      }
    }
  }

  if (mapTableName != nullptr)
  {
    for (SQLObject_ptr parent = x->getParent(); parent != nullptr; parent = parent->getParent())
    {
      if (instanceof <SQLObject, SQLSelectQueryBlock>(parent))
      {
        SQLTableSource_ptr from = (std::dynamic_pointer_cast<SQLSelectQueryBlock>(parent))->getFrom();
        string_list_ptr tmp = std::make_shared<std::list<string_ptr>>();
        tmp->push_back(mapTableName);
        tmp->push_back(ownerName);
        if (isTableSourceAlias(from, tmp))
        {
          mapTableName = nullptr;
        }
        break;
      }
    }
  }

  if (mapTableName != nullptr)
  {
    printName0(mapTableName);
  }
  else
  {
    if (instanceof <SQLExpr, SQLIdentifierExpr>(owner))
    {
      SQLIdentifierExpr_ptr ownerIdent = std::dynamic_pointer_cast<SQLIdentifierExpr>(owner);
      printName(
          ownerIdent,
          ownerIdent->getName(),
          this->shardingSupport && this->parameterized);
    }
    else
    {
      printExpr(owner, parameterized);
    }
  }
  print('.');
  string_ptr name = x->getName();
  if ("*" == name->c_str())
  {
    print0(name);
  }
  else
  {
    printName0(name);
  }
  return false;
}

bool SQLASTOutputVisitor::isTableSourceAlias(SQLTableSource_ptr from, string_list_ptr tableNames)
{
  string_ptr alias = from->getAlias();

  if (alias != nullptr)
  {
    for (string_ptr tableName : *tableNames)
    {
      if (StringUtils::equalsIgnoreCase(alias, tableName))
      {
        return true;
      }
    }

    if (alias->length() > 2 && alias->at(0) == '`' && alias->at(alias->length() - 1) == '`')
    {
      alias = make_string_ptr(alias->substr(1, alias->length() - 1));
      for (string_ptr tableName : *tableNames)
      {
        if (StringUtils::equalsIgnoreCase(alias, tableName))
        {
          return true;
        }
      }
    }
  }
  if (instanceof <SQLTableSource, SQLJoinTableSource>(from))
  {
    SQLJoinTableSource_ptr join = std::dynamic_pointer_cast<SQLJoinTableSource>(from);
    if (isTableSourceAlias(join->getLeft(), tableNames) ||
        isTableSourceAlias(join->getRight(), tableNames))
    {
      return true;
    }
    else
    {
      return false;
    }
  }
  return false;
}
bool SQLASTOutputVisitor::isTableSourceAlias(SQLTableSource_ptr from, string_ptr tableName)
{
  string_list_ptr tmp = std::make_shared<std::list<string_ptr>>();
  tmp->push_back(tableName);
  return isTableSourceAlias(from, tmp);
}

bool SQLASTOutputVisitor::visit(SQLQueryExpr_ptr x)
{
  SQLObject_ptr parent = x->getParent();
  if (instanceof <SQLObject, SQLSelect>(parent))
  {
    parent = parent->getParent();
  }

  SQLSelect_ptr subQuery = x->getSubQuery();
  if (instanceof <SQLObject, SQLInsertStatement_ValuesClause>(parent))
  {
    println();
    print('(');
    visit(subQuery);
    print(')');
    println();
  }
  else if ((instanceof <SQLObject, SQLStatement>(parent) &&
                           !(instanceof <SQLObject, OracleForStatement>(parent))) ||
           (instanceof <SQLObject, SQLSelectItem>(parent) &&
                           (instanceof <SQLObject, SQLPivot>(parent->getParent()) ||
                            instanceof <SQLObject, SQLUnpivot>(parent->getParent()))))
  {
    this->indentCount++;

    println();
    visit(subQuery);

    this->indentCount--;
  }
  else if (instanceof <SQLObject, SQLOpenStatement>(parent))
  {
    visit(subQuery);
  }
  else if (instanceof <SQLObject, SQLMethodInvokeExpr>(parent) &&
                          (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(parent))->getArguments()->size() == 1 &&
                          ((std::dynamic_pointer_cast<SQLMethodInvokeExpr>(parent))->methodNameHashCode64() == FnvHash::Constants::LATERAL ||
                           (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(parent))->methodNameHashCode64() == FnvHash::Constants::ARRAY))
  {
    this->indentCount++;
    println();
    visit(subQuery);
    this->indentCount--;
    println();
  }
  else if (instanceof <SQLObject, SQLMethodInvokeExpr>(parent) &&
                          (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(parent))->getArguments()->size() == 1)
  {
    this->indentCount++;
    println();
    visit(subQuery);
    this->indentCount--;
    println();
  }
  else
  {
    print('(');
    this->indentCount++;
    println();
    visit(subQuery);
    this->indentCount--;
    println();
    print(')');
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLSelectGroupByClause_ptr x)
{
  bool paren = DbType::oracle.name->c_str() == dbType->name->c_str() || x->isParen();
  bool rollup = (*x->isWithRollUp()) == BOOL::TRUE;
  bool cube = (*x->isWithCube()) == BOOL::TRUE;

  SQLExpr_list_ptr items = x->getItems();
  int itemSize = items->size();
  if (itemSize > 0)
  {
    print0(ucase ? make_string_ptr("GROUP BY ") : make_string_ptr("group by "));

    if (x->isDistinct())
    {
      print0(ucase ? make_string_ptr("DISTINCT ") : make_string_ptr("distinct "));
    }

    if (paren && rollup)
    {
      print0(ucase ? make_string_ptr("ROLLUP (") : make_string_ptr("rollup ("));
    }
    else if (paren && cube)
    {
      print0(ucase ? make_string_ptr("CUBE (") : make_string_ptr("cube ("));
    }
    this->indentCount++;
    auto it_items = items->begin();
    for (int i = 0; i < itemSize; ++i)
    {
      std::advance(it_items, i);
      SQLExpr_ptr item = *it_items;
      if (isPrettyFormat() && item->hasBeforeComment())
      {
        if (i != 0)
        {
          print(' ');
        }
        printlnComments(item->getBeforeCommentsDirect());
      }

      if (i != 0)
      {
        if (groupItemSingleLine)
        {
          println(", ");
        }
        else
        {
          if (instanceof <SQLExpr, SQLGroupingSetExpr>(item))
          {
            println();
          }
          else
          {
            print(", ");
          }
        }
      }
      if (instanceof <SQLExpr, SQLIntegerExpr>(item))
      {
        printInteger(std::dynamic_pointer_cast<SQLIntegerExpr>(item), BOOL::FALSE);
      }
      else if (instanceof <SQLExpr, MySqlOrderingExpr>(item) &&
                              (instanceof <SQLExpr, SQLIntegerExpr>(std::dynamic_pointer_cast<MySqlOrderingExpr>(item)->getExpr())))
      {
        MySqlOrderingExpr_ptr orderingExpr = std::dynamic_pointer_cast<MySqlOrderingExpr>(item);
        printInteger(std::dynamic_pointer_cast<SQLIntegerExpr>(orderingExpr->getExpr()), BOOL::FALSE);
        print(' ' + orderingExpr->getType()->name->c_str());
      }
      else
      {
        item->accept(SharedObject(SQLASTOutputVisitor));
      }

      SQLCommentHint_ptr hint = nullptr;
      if (instanceof <SQLExpr, SQLExprImpl>(item))
      {
        hint = (std::dynamic_pointer_cast<SQLExprImpl>(item))->getHint();
      }
      if (hint != nullptr)
      {
        hint->accept(SharedObject(SQLASTOutputVisitor));
      }
    }
    if (paren && (rollup || cube))
    {
      print(')');
    }
    this->indentCount--;
  }

  SQLExpr_ptr having = x->getHaving();
  if (having != nullptr)
  {
    printHaving(having);
  }

  if (x->isWithRollUp() && !paren)
  {
    print0(ucase ? make_string_ptr(" WITH ROLLUP") : make_string_ptr(" with rollup"));
  }

  if (x->isWithCube() && !paren)
  {
    print0(ucase ? make_string_ptr(" WITH CUBE") : make_string_ptr(" with cube"));
  }

  return false;
}

void SQLASTOutputVisitor::printHaving(SQLExpr_ptr having)
{
  println();
  print0(ucase ? make_string_ptr("HAVING ") : make_string_ptr("having "));
  having->accept(SharedObject(SQLASTOutputVisitor));
}

bool SQLASTOutputVisitor::visit(SQLSelect_ptr x)
{
  SQLHint_ptr headHint = x->getHeadHint();
  if (headHint != nullptr)
  {
    headHint->accept(SharedObject(SQLASTOutputVisitor));
    println();
  }

  SQLWithSubqueryClause_ptr withSubQuery = x->getWithSubQuery();
  if (withSubQuery != nullptr)
  {
    withSubQuery->accept(SharedObject(SQLASTOutputVisitor));
    println();
  }

  printQuery(x->getQuery());

  SQLOrderBy_ptr orderBy = x->getOrderBy();
  if (orderBy != nullptr)
  {
    println();
    orderBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLLimit_ptr limit = x->getLimit();
  if (limit != nullptr)
  {
    println();
    limit->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getHintsSize() > 0)
  {
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLHint, SQLObject>(x->getHints());
    printAndAccept(tmp1, make_string_ptr(""));
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLSelectQueryBlock_ptr x)
{
  if (isPrettyFormat() && x->hasBeforeComment())
  {
    printlnComments(x->getBeforeCommentsDirect());
  }

  print0(ucase ? make_string_ptr("SELECT ") : make_string_ptr("select "));

  if (x->getHintsSize() > 0)
  {
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLCommentHint, SQLObject>(x->getHints());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(' ');
  }

  bool informix = DbType::informix.name->c_str() == dbType->name->c_str();
  if (informix)
  {
    printFetchFirst(x);
  }

  int distinctOption = x->getDistionOption();
  if (SQLSetQuantifier::ALL == distinctOption)
  {
    print0(ucase ? make_string_ptr("ALL ") : make_string_ptr("all "));
  }
  else if (SQLSetQuantifier::DISTINCT == distinctOption)
  {
    print0(ucase ? make_string_ptr("DISTINCT ") : make_string_ptr("distinct "));
  }
  else if (SQLSetQuantifier::UNIQUE == distinctOption)
  {
    print0(ucase ? make_string_ptr("UNIQUE ") : make_string_ptr("unique "));
  }

  printSelectList(x->getSelectList());

  SQLExprTableSource_ptr into = x->getInto();
  if (into != nullptr)
  {
    println();
    print0(ucase ? make_string_ptr("INTO ") : make_string_ptr("into "));
    into->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLTableSource_ptr from = x->getFrom();
  if (from != nullptr)
  {
    println();

    bool printFrom = instanceof <SQLTableSource, SQLLateralViewTableSource>(from) && (std::dynamic_pointer_cast<SQLLateralViewTableSource>(from))->getTableSource() == nullptr;
    if (!printFrom)
    {
      print0(ucase ? make_string_ptr("FROM ") : make_string_ptr("from "));
    }
    printTableSource(from);
  }

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    printWhere(where);
  }

  printHierarchical(x);

  SQLSelectGroupByClause_ptr groupBy = x->getGroupBy();
  if (groupBy != nullptr)
  {
    println();
    visit(groupBy);
  }

  SQLWindow_list_ptr windows = x->getWindows();
  if (windows != nullptr && windows->size() > 0)
  {
    println();
    print0(ucase ? make_string_ptr("WINDOW ") : make_string_ptr("window "));
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLWindow, SQLObject>(windows);
    printAndAccept(tmp1, make_string_ptr(", "));
  }

  SQLOrderBy_ptr orderBy = x->getOrderBy();
  if (orderBy != nullptr)
  {
    println();
    orderBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLSelectOrderByItem_list_ptr distributeBy = x->getDistributeByDirect();
  if (distributeBy != nullptr && distributeBy->size() > 0)
  {
    println();
    print0(ucase ? make_string_ptr("DISTRIBUTE BY ") : make_string_ptr("distribute by "));
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(distributeBy);
    printAndAccept(tmp1, make_string_ptr(", "));
  }

  SQLSelectOrderByItem_list_ptr sortBy = x->getSortByDirect();
  if (sortBy != nullptr && sortBy->size() > 0)
  {
    println();
    print0(ucase ? make_string_ptr("SORT BY ") : make_string_ptr("sort by "));
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(sortBy);
    printAndAccept(tmp1, make_string_ptr(", "));
  }

  SQLSelectOrderByItem_list_ptr clusterBy = x->getClusterByDirect();
  if (clusterBy != nullptr && clusterBy->size() > 0)
  {
    println();
    print0(ucase ? make_string_ptr("CLUSTER BY ") : make_string_ptr("cluster by "));
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(clusterBy);
    printAndAccept(tmp1, make_string_ptr(", "));
  }

  if (!informix)
  {
    printFetchFirst(x);
  }

  if (x->isForUpdate())
  {
    println();
    print0(ucase ? make_string_ptr("FOR UPDATE") : make_string_ptr("for update"));
  }

  return false;
}

void SQLASTOutputVisitor::printWhere(SQLExpr_ptr where)
{
  println();
  print0(ucase ? make_string_ptr("WHERE ") : make_string_ptr("where "));

  string_list_ptr beforeComments = where->getBeforeCommentsDirect();
  if (beforeComments != nullptr && !beforeComments->empty() && isPrettyFormat())
  {
    printlnComments(beforeComments);
  }

  printExpr(where, parameterized);

  string_list_ptr afterComments = where->getAfterCommentsDirect();
  if (afterComments != nullptr && !afterComments->empty() && isPrettyFormat())
  {
    print(' ');
    printlnComment(afterComments);
  }
}

void SQLASTOutputVisitor::printFetchFirst(SQLSelectQueryBlock_ptr x)
{
  SQLLimit_ptr limit = x->getLimit();
  if (limit == nullptr)
  {
    return;
  }

  SQLExpr_ptr offset = limit->getOffset();
  SQLExpr_ptr first = limit->getRowCount();

  if (DbType::informix.name->c_str() == dbType->name->c_str())
  {
    if (offset != nullptr)
    {
      print0(ucase ? make_string_ptr("SKIP ") : make_string_ptr("skip "));
      offset->accept(SharedObject(SQLASTOutputVisitor));
    }

    print0(ucase ? make_string_ptr(" FIRST ") : make_string_ptr(" first "));
    first->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }
  else if (DbType::db2.name->c_str() == dbType->name->c_str() ||
           DbType::oracle.name->c_str() == dbType->name->c_str() ||
           DbType::sqlserver.name->c_str() == dbType->name->c_str())
  {
    // order by 语句必须在FETCH FIRST ROWS ONLY之前
    SQLObject_ptr parent = x->getParent();
    if (instanceof <SQLObject, SQLSelect>(parent))
    {
      SQLOrderBy_ptr orderBy = (std::dynamic_pointer_cast<SQLSelect>(parent))->getOrderBy();
      if (orderBy != nullptr && orderBy->getItems()->size() > 0)
      {
        println();
        print0(ucase ? make_string_ptr("ORDER BY ") : make_string_ptr("order by "));
        SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(orderBy->getItems());
        printAndAccept(tmp1, make_string_ptr(", "));
      }
    }

    println();

    if (offset != nullptr)
    {
      print0(ucase ? make_string_ptr("OFFSET ") : make_string_ptr("offset "));
      offset->accept(SharedObject(SQLASTOutputVisitor));
      print0(ucase ? make_string_ptr(" ROWS") : make_string_ptr(" rows"));
    }

    if (first != nullptr)
    {
      if (offset != nullptr)
      {
        print(' ');
      }
      if (DbType::sqlserver.name->c_str() == dbType->name->c_str() &&
          offset != nullptr)
      {
        print0(ucase ? make_string_ptr("FETCH NEXT ") : make_string_ptr("fetch next "));
      }
      else
      {
        print0(ucase ? make_string_ptr("FETCH FIRST ") : make_string_ptr("fetch first "));
      }
      first->accept(SharedObject(SQLASTOutputVisitor));
      print0(ucase ? make_string_ptr(" ROWS ONLY") : make_string_ptr(" rows only"));
    }
  }
  else
  {
    println();
    limit->accept(SharedObject(SQLASTOutputVisitor));
  }
}

bool SQLASTOutputVisitor::visit(SQLSelectItem_ptr x)
{
  if (x->isConnectByRoot())
  {
    print0(ucase ? make_string_ptr("CONNECT_BY_ROOT ") : make_string_ptr("connect_by_root "));
  }

  SQLExpr_ptr expr = x->getExpr();

  if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
  {
    printName0((std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->getName());
  }
  else if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
  {
    visit(std::dynamic_pointer_cast<SQLPropertyExpr>(expr));
  }
  else if (expr != nullptr)
  {
    printExpr(expr, parameterized);
  }

  string_ptr alias = x->getAlias();
  if (alias != nullptr && alias->length() > 0)
  {
    print0(ucase ? make_string_ptr(" AS ") : make_string_ptr(" as "));
    char c0 = alias->at(0);

    BOOL_ptr special = BOOL::FALSE;
    if (c0 != '"' && c0 != '\'' && c0 != '`' && c0 != '[')
    {
      for (int i = 1; i < alias->length(); ++i)
      {
        char ch = alias->at(i);
        if (ch < 256)
        {
          if (ch >= '0' && ch <= '9')
          {
            // skip
          }
          else if (ch >= 'a' && ch <= 'z')
          {
            // skip
          }
          else if (ch >= 'A' && ch <= 'Z')
          {
            // skip
          }
          else if (ch == '_' || ch == '$')
          {
            // skip
          }
          else
          {
            special = BOOL::TRUE;
          }
        }
      }
    }
    if ((!printNameQuote) && (!special))
    {
      print0(alias);
    }
    else
    {
      print(quote);

      string_ptr unquoteAlias = nullptr;
      if (c0 == '`' && alias->at(alias->length() - 1) == '`')
      {
        unquoteAlias = make_string_ptr(alias->substr(1, alias->length() - 1));
      }
      else if (c0 == '\'' && alias->at(alias->length() - 1) == '\'')
      {
        unquoteAlias = make_string_ptr(alias->substr(1, alias->length() - 1));
      }
      else if (c0 == '"' && alias->at(alias->length() - 1) == '"')
      {
        unquoteAlias = make_string_ptr(alias->substr(1, alias->length() - 1));
      }
      else
      {
        print0(alias);
      }

      if (unquoteAlias != nullptr)
      {
        print0(unquoteAlias);
      }

      print(quote);
    }

    return false;
  }

  string_list_ptr aliasList = x->getAliasList();
  if (aliasList == nullptr)
  {
    return false;
  }

  println();
  print0(ucase ? make_string_ptr("AS (") : make_string_ptr("as ("));

  int aliasSize = aliasList->size();
  if (aliasSize > 5)
  {
    this->indentCount++;
    println();
  }

  auto it_aliasList = aliasList->begin();
  for (int i = 0; i < aliasSize; ++i)
  {
    if (i != 0)
    {
      if (aliasSize > 5)
      {
        println(",");
      }
      else
      {
        print0(", ");
      }
    }
    std::advance(it_aliasList, i);
    print0(*it_aliasList);
  }

  if (aliasSize > 5)
  {
    this->indentCount--;
    println();
  }
  print(')');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLOrderBy_ptr x)
{
  SQLSelectOrderByItem_list_ptr items = x->getItems();

  if (items->size() > 0)
  {
    if (x->isSiblings())
    {
      print0(ucase ? make_string_ptr("ORDER SIBLINGS BY ") : make_string_ptr("order siblings by "));
    }
    else
    {
      print0(ucase ? make_string_ptr("ORDER BY ") : make_string_ptr("order by "));
    }

    auto it_items = items->begin();
    for (int i = 0, size = items->size(); i < size; ++i)
    {
      if (i != 0)
      {
        print0(", ");
      }
      std::advance(it_items, i);
      SQLSelectOrderByItem_ptr item = *it_items;
      visit(item);
    }
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLSelectOrderByItem_ptr x)
{
  SQLExpr_ptr expr = x->getExpr();

  if (instanceof <SQLExpr, SQLIntegerExpr>(expr))
  {
    print((std::dynamic_pointer_cast<SQLIntegerExpr>(expr))->getNumber());
  }
  else
  {
    printExpr(expr, parameterized);
  }

  SQLOrderingSpecification_ptr type = x->getType();
  if (type != nullptr)
  {
    print(' ');
    print0(ucase ? type->name : type->nameLCase);
  }

  string_ptr collate = x->getCollate();
  if (collate != nullptr)
  {
    print0(ucase ? make_string_ptr(" COLLATE ") : make_string_ptr(" collate "));
    print0(collate);
  }

  SQLSelectOrderByItem_NullsOrderType_ptr nullsOrderType = x->getNullsOrderType();
  if (nullsOrderType != nullptr)
  {
    print(' ');
    print0(nullsOrderType->toFormalString());
  }

  SQLCommentHint_ptr hint = x->getHint();
  if (hint != nullptr)
  {
    visit(hint);
  }

  return false;
}

void SQLASTOutputVisitor::addTable(string_ptr table)
{
  if (tables == nullptr)
  {
    if (this->table == nullptr)
    {
      this->table = table;
      return;
    }
    else
    {
      tables = std::make_shared<std::set<string_ptr>>();
      tables->insert(this->table);
    }
  }
  this->tables->insert(table);
}

void SQLASTOutputVisitor::printTableSourceExpr(SQLExpr_ptr expr)
{
  if (exportTables)
  {
    addTable(expr->toString());
  }

  if ((*SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputDesensitize)))) == BOOL::TRUE)
  {
    string_ptr ident = nullptr;
    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      ident = (std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->getName();
    }
    else if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
    {
      SQLPropertyExpr_ptr propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(expr);
      propertyExpr->getOwner()->accept(SharedObject(SQLASTOutputVisitor));
      print('.');

      ident = propertyExpr->getName();
    }

    if (ident != nullptr)
    {
      string_ptr desensitizeTable = SQLUtils::desensitizeTable(ident);
      print0(desensitizeTable);
      return;
    }
  }

  if (tableMapping != nullptr && instanceof <SQLExpr, SQLName>(expr))
  {
    string_ptr tableName;
    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      tableName = (std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->normalizedName();
    }
    else if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
    {
      tableName = (std::dynamic_pointer_cast<SQLPropertyExpr>(expr))->normalizedName();
    }
    else
    {
      tableName = expr->toString();
    }

    auto destTableName = tableMapping->find(tableName->c_str());
    if (destTableName == tableMapping->end())
    {
      if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
      {
        SQLPropertyExpr_ptr propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(expr);
        string_ptr propName = propertyExpr->getName();
        destTableName = tableMapping->find(propName->c_str());
        if (destTableName == tableMapping->end() && propName->length() > 2 &&
            propName->at(0) == '`' &&
            propName->at(propName->length() - 1) == '`')
        {
          destTableName = tableMapping->find(propName->substr(1, propName->length() - 1));
        }

        if (destTableName != tableMapping->end())
        {
          propertyExpr->getOwner()->accept(SharedObject(SQLASTOutputVisitor));
          print('.');
          print(destTableName->second);
          return;
        }
      }
      else if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
      {
        bool quote = tableName->length() > 2 &&
                     tableName->at(0) == '`' &&
                     tableName->at(tableName->length() - 1) == '`';
        if (quote)
        {
          destTableName = tableMapping->find(tableName->substr(1, tableName->length() - 1));
        }
      }
    }
    if (destTableName != tableMapping->end())
    {
      tableName = destTableName->second;
      printName0(tableName);
      return;
    }
  }

  if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
  {
    SQLIdentifierExpr_ptr identifierExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
    string_ptr name = identifierExpr->getName();
    if (!this->parameterized)
    {
      printName0(name);
      return;
    }

    bool shardingSupport = this->shardingSupport && this->parameterized;

    if (shardingSupport)
    {
      string_ptr nameUnwrappe = unwrapShardingTable(name);

      if (!(name->c_str() == nameUnwrappe->c_str()))
      {
        incrementReplaceCunt();
      }

      printName0(nameUnwrappe);
    }
    else
    {
      printName0(name);
    }
  }
  else if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
  {
    SQLPropertyExpr_ptr propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(expr);
    SQLExpr_ptr owner = propertyExpr->getOwner();

    if (instanceof <SQLExpr, SQLIdentifierExpr>(owner))
    {
      SQLIdentifierExpr_ptr identOwner = std::dynamic_pointer_cast<SQLIdentifierExpr>(owner);

      string_ptr ownerName = identOwner->getName();
      if (!this->parameterized)
      {
        printName0(identOwner->getName());
      }
      else
      {
        if (shardingSupport)
        {
          ownerName = unwrapShardingTable(ownerName);
        }
        printName0(ownerName);
      }
    }
    else
    {
      printExpr(owner);
    }
    print('.');

    string_ptr name = propertyExpr->getName();
    if (!this->parameterized)
    {
      printName0(propertyExpr->getName());
      return;
    }

    bool shardingSupport = this->shardingSupport && this->parameterized;

    if (shardingSupport)
    {
      string_ptr nameUnwrappe = unwrapShardingTable(name);

      if (!(name->c_str() == nameUnwrappe->c_str()))
      {
        incrementReplaceCunt();
      }

      printName0(nameUnwrappe);
    }
    else
    {
      printName0(name);
    }
  }
  else if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
  {
    visit(std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr));
  }
  else
  {
    expr->accept(SharedObject(SQLASTOutputVisitor));
  }
}

bool SQLASTOutputVisitor::visit(SQLExprTableSource_ptr x)
{
  printTableSourceExpr(x->getExpr());

  SQLTableSampling_ptr sampling = x->getSampling();
  if (sampling != nullptr)
  {
    print(' ');
    sampling->accept(SharedObject(SQLASTOutputVisitor));
  }

  string_ptr alias = x->getAlias();
  SQLName_list_ptr columns = x->getColumnsDirect();
  if (alias != nullptr)
  {
    print(' ');
    if (columns != nullptr && columns->size() > 0)
    {
      print0(ucase ? make_string_ptr(" AS ") : make_string_ptr(" as "));
    }
    print0(alias);
  }

  if (columns != nullptr && columns->size() > 0)
  {
    print(" (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(columns);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  SQLPivot_ptr pivot = x->getPivot();
  if (pivot != nullptr)
  {
    println();
    pivot->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLUnpivot_ptr unpivot = x->getUnpivot();
  if (unpivot != nullptr)
  {
    println();
    unpivot->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (isPrettyFormat() && x->hasAfterComment())
  {
    print(' ');
    printlnComment(x->getAfterCommentsDirect());
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLSelectStatement_ptr stmt)
{
  SQLCommentHint_list_ptr headHints = stmt->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      visit(std::dynamic_pointer_cast<SQLCommentHint>(hint));
      println();
    }
  }

  if (stmt->hasBeforeComment())
  {
    printlnComments(stmt->getBeforeCommentsDirect());
  }

  SQLSelect_ptr select = stmt->getSelect();
  if (select != nullptr)
  {
    this->visit(select);
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLVariantRefExpr_ptr x)
{
  int index = x->getIndex();

  if (index < 0 || inputParameters == nullptr || index >= inputParameters->size())
  {
    print0(x->getName());
    return false;
  }

  auto it_inputParameters = inputParameters->begin();
  std::advance(it_inputParameters, index);
  Object_ptr param = *it_inputParameters;

  SQLObject_ptr parent = x->getParent();

  BOOL_ptr in;
  if (instanceof <SQLObject, SQLInListExpr>(parent))
  {
    in = BOOL::TRUE;
  }
  else if (instanceof <SQLObject, SQLBinaryOpExpr>(parent))
  {
    SQLBinaryOpExpr_ptr binaryOpExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(parent);
    if (binaryOpExpr->getOperator()->name->c_str() == SQLBinaryOperator::Equality.name->c_str())
    {
      in = BOOL::TRUE;
    }
    else
    {
      in = BOOL::FALSE;
    }
  }
  else
  {
    in = BOOL::FALSE;
  }

  // if (in && instanceof<Object,Collection>(param)  ) {
  //     BOOL_ptr first = BOOL::TRUE;
  //     for (Object item : (Collection) param) {
  //         if (!first) {
  //             print0(", ");
  //         }
  //         printParameter(item);
  //         first = BOOL::FALSE;
  //     }
  // } else {
  //     printParameter(param);
  // }
  return false;
}

void SQLASTOutputVisitor::printParameter(Object_ptr param)
{
  if (param == nullptr)
  {
    print0(ucase ? make_string_ptr("NULL") : make_string_ptr("nullptr"));
    return;
  }

  // if (param instanceof Number //
  //         || param instanceof BOOL_ptr || param instanceof java.time.temporal.Temporal) {
  //     print0(param.toString());
  //     return;
  // }

  // if (param instanceof string_ptr) {
  //     SQLCharExpr_ptr charExpr = new SQLCharExpr_ptr((string_ptr) param);
  //     visit(charExpr);
  //     return;
  // }

  // if (param instanceof time_t) {
  //     print((time_t) param);
  //     return;
  // }

  // if (param instanceof InputStream) {
  //     print0("'<InputStream>'");
  //     return;
  // }

  // if (param instanceof Reader) {
  //     print0("'<Reader>'");
  //     return;
  // }

  // if (param instanceof Blob) {
  //     print0("'<Blob>'");
  //     return;
  // }

  // if (param instanceof NClob) {
  //     print0("'<NClob>'");
  //     return;
  // }

  // if (param instanceof Clob) {
  //     print0("'<Clob>'");
  //     return;
  // }

  // if (param instanceof byte[]) {
  //     byte[] bytes = (byte[]) param;
  //     int bytesLen = bytes.length;
  //     char[] chars = new char[bytesLen * 2 + 3];
  //     chars[0] = 'x';
  //     chars[1] = '\'';
  //     for (int i = 0; i < bytes.length; i++) {
  //         int a = bytes[i] & 0xFF;
  //         int b0 = a >> 4;
  //         int b1 = a & 0xf;

  //         chars[i * 2 + 2] = (char) (b0 + (b0 < 10 ? 48 : 55)); //hexChars[b0];
  //         chars[i * 2 + 3] = (char) (b1 + (b1 < 10 ? 48 : 55));
  //     }
  //     chars[chars.length - 1] = '\'';
  //     print0(new string_ptr(chars));
  //     return;
  // }

  // if (param instanceof Character) {
  //     print('\'');
  //     print((Character) param);
  //     print('\'');
  //     return;
  // }

  std::string tmp = "'";
  tmp += param->getClass()->c_str();
  tmp += "'";
  print0(make_string_ptr(tmp));
}

bool SQLASTOutputVisitor::visit(SQLDropTableStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      visit(std::dynamic_pointer_cast<SQLCommentHint>(hint));
      println();
    }
  }

  if (x->hasBeforeComment())
  {
    printlnComments(x->getBeforeCommentsDirect());
  }

  print0(ucase ? make_string_ptr("DROP ") : make_string_ptr("drop "));
  SQLCommentHint_list_ptr hints = x->getHints();
  if (hints != nullptr)
  {
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLCommentHint, SQLObject>(hints);
    printAndAccept(tmp1, make_string_ptr(" "));
    print(' ');
  }

  if (x->isExternal())
  {
    print0(ucase ? make_string_ptr("EXTERNAL ") : make_string_ptr("external "));
  }

  if (x->isDropPartition())
  {
    print0(ucase ? make_string_ptr("PARTITIONED ") : make_string_ptr("partitioned "));
  }

  if (x->isTemporary())
  {
    print0(ucase ? make_string_ptr("TEMPORARY TABLE ") : make_string_ptr("temporary table "));
  }
  else
  {
    print0(ucase ? make_string_ptr("TABLE ") : make_string_ptr("table "));
  }

  if (x->isIfExists())
  {
    print0(ucase ? make_string_ptr("IF EXISTS ") : make_string_ptr("if exists "));
  }

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExprTableSource, SQLObject>(x->getTableSources());
  printAndAccept(tmp1, make_string_ptr(", "));

  if (x->isCascade())
  {
    printCascade();
  }

  if (x->isRestrict())
  {
    print0(ucase ? make_string_ptr(" RESTRICT") : make_string_ptr(" restrict"));
  }

  if (x->isPurge())
  {
    print0(ucase ? make_string_ptr(" PURGE") : make_string_ptr(" purge"));
  }

  if (x->getWhere() != nullptr)
  {
    print0(ucase ? make_string_ptr(" WHERE ") : make_string_ptr(" where "));
    x->getWhere()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

void SQLASTOutputVisitor::printCascade()
{
  print0(ucase ? make_string_ptr(" CASCADE") : make_string_ptr(" cascade"));
}

bool SQLASTOutputVisitor::visit(SQLDropViewStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      visit(std::dynamic_pointer_cast<SQLCommentHint>(hint));
      println();
    }
  }

  if (x->hasBeforeComment())
  {
    printlnComments(x->getBeforeCommentsDirect());
  }

  print0(ucase ? make_string_ptr("DROP VIEW ") : make_string_ptr("drop view "));

  if (x->isIfExists())
  {
    print0(ucase ? make_string_ptr("IF EXISTS ") : make_string_ptr("if exists "));
  }

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExprTableSource, SQLObject>(x->getTableSources());
  printAndAccept(tmp1, make_string_ptr(", "));

  if (x->isCascade())
  {
    printCascade();
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLDropEventStatement_ptr x)
{
  print0(ucase ? make_string_ptr("DROP EVENT ") : make_string_ptr("drop event "));

  if (x->isIfExists())
  {
    print0(ucase ? make_string_ptr("IF EXISTS ") : make_string_ptr("if exists "));
  }

  printExpr(x->getName(), parameterized);
  return false;
}

bool SQLASTOutputVisitor::visit(SQLDropResourceStatement_ptr x)
{
  print0(ucase ? make_string_ptr("DROP RESOURCE ") : make_string_ptr("drop resource "));

  if (x->isIfExists())
  {
    print0(ucase ? make_string_ptr("IF EXISTS ") : make_string_ptr("if exists "));
  }

  printExpr(x->getName(), parameterized);
  return false;
}

bool SQLASTOutputVisitor::visit(SQLDropResourceGroupStatement_ptr x)
{
  print0(ucase ? make_string_ptr("DROP RESOURCE GROUP ") : make_string_ptr("drop resource group "));

  if (x->isIfExists())
  {
    print0(ucase ? make_string_ptr("IF EXISTS ") : make_string_ptr("if exists "));
  }

  printExpr(x->getName(), parameterized);
  return false;
}

bool SQLASTOutputVisitor::visit(SQLListResourceGroupStatement_ptr x)
{
  print0(ucase ? make_string_ptr("LIST RESOURCE GROUP") : make_string_ptr("list resource group"));

  return false;
}

bool SQLASTOutputVisitor::visit(SQLColumnDefinition_ptr x)
{
  BOOL_ptr parameterized = this->parameterized;
  this->parameterized = BOOL::FALSE;

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  SQLDataType_ptr dataType = x->getDataType();
  if (dataType != nullptr)
  {
    print(' ');
    dataType->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getDefaultExpr() != nullptr)
  {
    visitColumnDefault(x);
  }

  if (x->isAutoIncrement())
  {
    print0(ucase ? make_string_ptr(" AUTO_INCREMENT") : make_string_ptr(" auto_increment"));
  }

  AutoIncrementType_ptr sequenceType = x->getSequenceType();
  if (sequenceType != nullptr)
  {
    print0(" ");
    print0(ucase ? sequenceType->getKeyword() : StringUtils::toLower(sequenceType->getKeyword()));
  }

  for (SQLColumnConstraint_ptr item : *x->getConstraints())
  {
    bool newLine = instanceof <SQLColumnConstraint, SQLForeignKeyConstraint>(item) //
                                  ||
        instanceof <SQLColumnConstraint, SQLPrimaryKey>(item) //
                       ||
        instanceof <SQLColumnConstraint, SQLColumnCheck>(item) //
                       || item->getName() != nullptr;
    if (newLine)
    {
      this->indentCount++;
      println();
    }
    else
    {
      print(' ');
    }

    item->accept(SharedObject(SQLASTOutputVisitor));

    if (newLine)
    {
      this->indentCount--;
    }
  }

  SQLExpr_ptr generatedAlawsAs = x->getGeneratedAlawsAs();
  if (generatedAlawsAs != nullptr)
  {
    print0(ucase ? make_string_ptr(" GENERATED ALWAYS AS ") : make_string_ptr(" generated always as "));
    printExpr(generatedAlawsAs, parameterized);
  }

  Identity_ptr identity = x->getIdentity();
  if (identity != nullptr)
  {
    if (dbType->name->c_str() == DbType::h2.name->c_str())
    {
      print0(ucase ? make_string_ptr(" AS ") : make_string_ptr(" as "));
    }
    else
    {
      print(' ');
    }
    identity->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isVirtual())
  {
    print0(ucase ? make_string_ptr(" VIRTUAL") : make_string_ptr(" virtual"));
  }

  if (x->isVisible())
  {
    print0(ucase ? make_string_ptr(" VISIBLE") : make_string_ptr(" visible"));
  }

  BOOL_ptr enable = x->getEnable();
  if (enable != nullptr)
  {
    if (enable)
    {
      print0(ucase ? make_string_ptr(" ENABLE") : make_string_ptr(" enable"));
    }
  }

  if (x->getComment() != nullptr)
  {
    print0(ucase ? make_string_ptr(" COMMENT ") : make_string_ptr(" comment "));
    x->getComment()->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLAssignItem_list_ptr mappedBy = x->getMappedByDirect();
  if (mappedBy != nullptr && mappedBy->size() > 0)
  {
    print0(ucase ? make_string_ptr(" MAPPED BY (") : make_string_ptr(" mapped by ("));
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(mappedBy);
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(ucase ? make_string_ptr(")") : make_string_ptr(")"));
  }

  SQLAssignItem_list_ptr colProperties = x->getColPropertiesDirect();
  if (colProperties != nullptr && colProperties->size() > 0)
  {
    print0(ucase ? make_string_ptr(" COLPROPERTIES (") : make_string_ptr(" colproperties ("));
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(colProperties);
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(ucase ? make_string_ptr(")") : make_string_ptr(")"));
  }

  if (x->getEncode() != nullptr)
  {
    print0(ucase ? make_string_ptr(" ENCODE=") : make_string_ptr(" encode="));
    x->getEncode()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getCompression() != nullptr)
  {
    print0(ucase ? " COMPRESSION=" : " compression=");
    x->getCompression()->accept(SharedObject(SQLASTOutputVisitor));
  }

  this->parameterized = parameterized;

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(Identity_ptr x)
{
  print0(ucase ? "IDENTITY" : "identity");
  int seed = x->getSeed();
  // if (seed != nullptr)
  // {
  print0(" (");
  print(seed);
  print0(", ");
  print(x->getIncrement());
  print(')');
  // }
  return false;
}

void SQLASTOutputVisitor::visitColumnDefault(SQLColumnDefinition_ptr x)
{
  print0(ucase ? " DEFAULT " : " default ");
  printExpr(x->getDefaultExpr(), BOOL::FALSE);
}

bool SQLASTOutputVisitor::visit(SQLDeleteStatement_ptr x)
{
  SQLTableSource_ptr from = x->getFrom();
  string_ptr alias = x->getAlias();

  if (from == nullptr)
  {
    print0(ucase ? "DELETE FROM " : "delete from ");
    printTableSourceExpr(x->getTableName());

    if (alias != nullptr)
    {
      print(' ');
      print0(alias);
    }
  }
  else
  {
    print0(ucase ? "DELETE " : "delete ");
    printTableSourceExpr(x->getTableName());
    print0(ucase ? " FROM " : " from ");
    from->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    println();
    print0(ucase ? "WHERE " : "where ");
    this->indentCount++;
    where->accept(SharedObject(SQLASTOutputVisitor));
    this->indentCount--;
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLCurrentOfCursorExpr_ptr x)
{
  print0(ucase ? "CURRENT OF " : "current of ");
  printExpr(x->getCursorName(), parameterized);
  return false;
}

bool SQLASTOutputVisitor::visit(SQLInsertStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
      println();
    }
  }

  if (x->getInsertBeforeCommentsDirect() != nullptr)
  {
    printlnComments(x->getInsertBeforeCommentsDirect());
  }

  SQLWithSubqueryClause_ptr with = x->getWith();
  if (with != nullptr)
  {
    visit(with);
    println();
  }

  if (x->isUpsert())
  {
    print0(ucase ? "UPSERT INTO " : "upsert into ");
  }
  else
  {
    if (x->isOverwrite() && dbType->name->c_str() == DbType::odps.name->c_str())
    {
      print0(ucase ? "INSERT OVERWRITE " : "insert overwrite ");
    }
    else
    {
      print0(ucase ? "INSERT INTO " : "insert into ");
    }
  }

  x->getTableSource()->accept(SharedObject(SQLASTOutputVisitor));

  string_ptr columnsString = x->getColumnsString();
  if (columnsString != nullptr)
  {
    print0(columnsString);
  }
  else
  {
    printInsertColumns(x->getColumns());
  }

  if (!x->getValuesList()->empty())
  {
    println();
    print0(ucase ? "VALUES " : "values ");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLInsertStatement_ValuesClause, SQLObject>(x->getValuesList());
    printAndAccept(tmp1, make_string_ptr(", "));
  }
  else
  {
    if (x->getQuery() != nullptr)
    {
      println();
      x->getQuery()->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  return false;
}

void SQLASTOutputVisitor::printInsertColumns(SQLExpr_list_ptr columns)
{
  int size = columns->size();
  LOG_DEBUG << "size:" << size;
  if (size > 0)
  {
    if (size > 5)
    {
      this->indentCount++;
      println();
    }
    else
    {
      printChar(' ');
      // print(' ');
    }
    print('(');
    auto it_columns = columns->begin();
    for (int i = 0; i < size; ++i)
    {
      if (i != 0)
      {
        if (i % 5 == 0)
        {
          println();
        }
        print0(", ");
      }

      std::advance(it_columns, i);
      SQLExpr_ptr column = *it_columns;
      if (instanceof <SQLExpr, SQLIdentifierExpr>(column))
      {
        visit(std::dynamic_pointer_cast<SQLIdentifierExpr>(column));
      }
      else
      {
        printExpr(column, parameterized);
      }

      Object_ptr dataType = column->getAttribute("dataType");
      if (dataType != nullptr)
      {
        print(' ');
        print(dataType->getString());
      }
    }
    print(')');
    if (size > 5)
    {
      this->indentCount--;
    }
  }
}

bool SQLASTOutputVisitor::visit(SQLUpdateSetItem_ptr x)
{
  printExpr(x->getColumn(), parameterized);
  print0(" = ");
  printExpr(x->getValue(), parameterized);
  return false;
}

bool SQLASTOutputVisitor::visit(SQLUpdateStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
      println();
    }
  }

  if (x->getWith() != nullptr)
  {
    x->getWith()->accept(SharedObject(SQLASTOutputVisitor));
    println();
  }

  print0(ucase ? "UPDATE " : "update ");

  printTableSource(x->getTableSource());

  println();
  print0(ucase ? "SET " : "set ");
  auto it_getItems = x->getItems()->begin();
  for (int i = 0, size = x->getItems()->size(); i < size; ++i)
  {
    if (i != 0)
    {
      print0(", ");
    }
    std::advance(it_getItems, i);
    SQLUpdateSetItem_ptr item = *it_getItems;
    visit(item);
  }

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    println();
    indentCount++;
    print0(ucase ? "WHERE " : "where ");
    printExpr(where, parameterized);
    indentCount--;
  }

  return false;
}

void SQLASTOutputVisitor::printTableElements(SQLTableElement_list_ptr tableElementList)
{
  int size = tableElementList->size();
  if (size == 0)
  {
    return;
  }

  print0(" (");

  this->indentCount++;
  println();
  auto it_tableElementList = tableElementList->begin();
  for (int i = 0; i < size; ++i)
  {
    std::advance(it_tableElementList, i);
    SQLTableElement_ptr element = *it_tableElementList;
    element->accept(SharedObject(SQLASTOutputVisitor));

    if (i != size - 1)
    {
      print(',');
    }
    if (this->isPrettyFormat() && element->hasAfterComment())
    {
      print(' ');
      printlnComment(element->getAfterCommentsDirect());
    }

    if (i != size - 1)
    {
      println();
    }
  }
  this->indentCount--;
  println();
  print(')');
}

bool SQLASTOutputVisitor::visit(SQLCreateTableStatement_ptr x)
{
  printCreateTable(x, BOOL::TRUE);

  SQLPartitionBy_ptr partitionBy = x->getPartitioning();
  if (partitionBy != nullptr)
  {
    println();
    print0(ucase ? "PARTITION BY " : "partition by ");
    partitionBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLAssignItem_list_ptr options = x->getTableOptions();
  if (options->size() > 0)
  {
    println();
    print0(ucase ? "WITH (" : "with (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(options);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  SQLName_ptr tablespace = x->getTablespace();
  if (tablespace != nullptr)
  {
    println();
    print0(ucase ? "TABLESPACE " : "tablespace ");
    tablespace->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr engine = x->getEngine();
  if (engine != nullptr)
  {
    print0(ucase ? " ENGINE = " : " engine = ");
    engine->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

void SQLASTOutputVisitor::printCreateTable(SQLCreateTableStatement_ptr x, BOOL_ptr printSelect)
{
  print0(ucase ? "CREATE " : "create ");

  if (x->isExternal())
  {
    print0(ucase ? "EXTERNAL " : "external ");
  }

  SQLCreateTableStatement::Type tableType = x->getType();
  if (SQLCreateTableStatement::Type::GLOBAL_TEMPORARY == tableType)
  {
    print0(ucase ? "GLOBAL TEMPORARY " : "global temporary ");
  }
  else if (SQLCreateTableStatement::Type::LOCAL_TEMPORARY == tableType)
  {
    print0(ucase ? "LOCAL TEMPORARY " : "local temporary ");
  }
  else if (SQLCreateTableStatement::Type::SHADOW == tableType)
  {
    print0(ucase ? "SHADOW " : "shadow ");
  }

  if (x->isDimension())
  {
    print0(ucase ? "DIMENSION " : "dimension ");
  }

  print0(ucase ? "TABLE " : "table ");

  if (x->isIfNotExists())
  {
    print0(ucase ? "IF NOT EXISTS " : "if not exists ");
  }

  printTableSourceExpr(x->getName());

  printTableElements(x->getTableElementList());

  SQLExprTableSource_ptr inherits = x->getInherits();
  if (inherits != nullptr)
  {
    print0(ucase ? " INHERITS (" : " inherits (");
    inherits->accept(SharedObject(SQLASTOutputVisitor));
    print(')');
  }

  SQLExpr_ptr storedAs = x->getStoredAs();
  if (storedAs != nullptr)
  {
    print0(ucase ? " STORE AS " : " store as ");
    printExpr(storedAs, parameterized);
  }

  SQLSelect_ptr select = x->getSelect();
  if (printSelect && select != nullptr)
  {
    println();
    print0(ucase ? "AS" : "as");

    println();
    visit(select);
  }
}

BOOL_ptr SQLASTOutputVisitor::visit(SQLUniqueConstraint_ptr x)
{
  if (x->getName() != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    x->getName()->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }

  print0(ucase ? "UNIQUE (" : "unique (");
  SQLSelectOrderByItem_list_ptr columns = x->getColumns();
  auto it_columns = columns->begin();
  for (int i = 0, size = columns->size(); i < size; ++i)
  {
    if (i != 0)
    {
      print0(", ");
    }
    std::advance(it_columns, i);
    visit(*it_columns);
  }
  print(')');
  return BOOL::FALSE;
}

bool SQLASTOutputVisitor::visit(SQLNotNullConstraint_ptr x)
{
  SQLName_ptr name = x->getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }
  print0(ucase ? "NOT NULL" : "not nullptr");

  SQLCommentHint_list_ptr hints = x->hints;
  if (hints != nullptr)
  {
    print(' ');
    for (SQLCommentHint_ptr hint : *hints)
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLNullConstraint_ptr x)
{
  SQLName_ptr name = x->getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }
  print0(ucase ? "NULL" : "nullptr");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLUnionQuery_ptr x)
{
  SQLUnionOperator_ptr operator_ = x->getOperator();

  SQLSelectQuery_list_ptr relations = x->getRelations();
  if (relations->size() > 2)
  {
    auto it_relations = relations->begin();
    for (int i = 0; i < relations->size(); i++)
    {
      if (i != 0)
      {
        println();
        print0(ucase ? operator_->name : operator_->nameLCase);
        println();
      }

      std::advance(it_relations, i);
      SQLSelectQuery_ptr item = *it_relations;
      item->accept(SharedObject(SQLASTOutputVisitor));
    }

    SQLOrderBy_ptr orderBy = x->getOrderBy();
    if (orderBy != nullptr)
    {
      println();
      orderBy->accept(SharedObject(SQLASTOutputVisitor));
    }

    SQLLimit_ptr limit = x->getLimit();
    if (limit != nullptr)
    {
      println();
      limit->accept(SharedObject(SQLASTOutputVisitor));
    }
    return false;
  }

  SQLSelectQuery_ptr left = x->getLeft();
  SQLSelectQuery_ptr right = x->getRight();

  bool bracket = x->isParenthesized() && 
  !(instanceof <SQLObject, SQLUnionQueryTableSource>(x->getParent()));

  SQLOrderBy_ptr orderBy = x->getOrderBy();
  if ((!bracket) && instanceof <SQLSelectQuery, SQLUnionQuery>(left) &&
                                   (std::dynamic_pointer_cast<SQLUnionQuery>(left))->getOperator()->name->c_str() == operator_->name->c_str() &&
                                   !right->isParenthesized() &&
                                   x->getLimit() == nullptr &&
                                   orderBy == nullptr)
  {
    SQLUnionQuery_ptr leftUnion = std::dynamic_pointer_cast<SQLUnionQuery>(left);

    if (instanceof <SQLSelectQuery, SQLSelectQueryBlock>(right))
    {
      SQLSelectQueryBlock_ptr rightQueryBlock = std::dynamic_pointer_cast<SQLSelectQueryBlock>(right);
      if (rightQueryBlock->getOrderBy() != nullptr || rightQueryBlock->getLimit() != nullptr)
      {
        rightQueryBlock->setParenthesized(BOOL::TRUE);
      }
    }

    SQLSelectQuery_list_ptr rights = std::make_shared<std::list<SQLSelectQuery_ptr>>();
    rights->push_back(right);

    if (leftUnion->getRelations()->size() > 2)
    {
      // rights.addAll(leftUnion.getRelations());
      Utils::ListAddAll<SQLSelectQuery, SQLSelectQuery>(leftUnion->getRelations(), rights);
    }
    else
    {
      for (;;)
      {
        SQLSelectQuery_ptr leftLeft = leftUnion->getLeft();
        SQLSelectQuery_ptr leftRight = leftUnion->getRight();

        if (instanceof <SQLSelectQuery, SQLSelectQueryBlock>(leftRight))
        {
          SQLSelectQueryBlock_ptr leftRightQueryBlock = std::dynamic_pointer_cast<SQLSelectQueryBlock>(leftRight);
          if (leftRightQueryBlock->getOrderBy() != nullptr ||
              leftRightQueryBlock->getLimit() != nullptr)
          {
            leftRightQueryBlock->setParenthesized(BOOL::TRUE);
          }
        }

        if ((!leftUnion->isParenthesized()) &&
                leftUnion->getOrderBy() == nullptr &&
                (!leftLeft->isParenthesized()) &&
                (!leftRight->isParenthesized()) &&
            instanceof <SQLSelectQuery, SQLUnionQuery>(leftLeft) &&
                           (std::dynamic_pointer_cast<SQLUnionQuery>(leftLeft))->getOperator()->name->c_str() == operator_->name->c_str())
        {
          rights->push_back(leftRight);
          leftUnion = std::dynamic_pointer_cast<SQLUnionQuery>(leftLeft);
          continue;
        }
        else
        {
          rights->push_back(leftRight);
          rights->push_back(leftLeft);
        }
        break;
      }
    }

    auto it_rights = rights->begin();
    for (int i = rights->size() - 1; i >= 0; i--)
    {
      std::advance(it_rights, i);
      SQLSelectQuery_ptr item = *it_rights;
      item->accept(SharedObject(SQLASTOutputVisitor));

      if (i > 0)
      {
        println();
        print0(ucase ? operator_->name : operator_->nameLCase);
        println();
      }
    }
    return false;
  }

  if (bracket)
  {
    print('(');
  }

  if (left != nullptr)
  {
    for (;;)
    {
      if (left->getClass()->c_str() == SQLUnionQuery::class_->c_str())
      {
        SQLUnionQuery_ptr leftUnion = std::dynamic_pointer_cast<SQLUnionQuery>(left);
        if (leftUnion->getRelations()->size() > 2)
        {
          visit(leftUnion);
        }
        else
        {
          SQLSelectQuery_ptr leftLeft = leftUnion->getLeft();
          SQLSelectQuery_ptr leftRigt = leftUnion->getRight();
          if ((!leftUnion->isParenthesized()) &&
              instanceof <SQLSelectQuery, SQLSelectQueryBlock>(leftUnion->getRight()) &&
                             leftUnion->getLeft() != nullptr &&
                             leftUnion->getLimit() == nullptr &&
                             leftUnion->getOrderBy() == nullptr)
          {
            if (leftLeft->getClass()->c_str() == SQLUnionQuery::class_->c_str())
            {
              visit(std::dynamic_pointer_cast<SQLUnionQuery>(leftLeft));
            }
            else
            {
              printQuery(leftLeft);
            }
            println();
            print0(ucase ? leftUnion->getOperator()->name : leftUnion->getOperator()->nameLCase);
            println();
            leftRigt->accept(SharedObject(SQLASTOutputVisitor));
          }
          else
          {
            visit(leftUnion);
          }
        }
      }
      else
      {
        left->accept(SharedObject(SQLASTOutputVisitor));
      }
      break;
    }
  }

  if (right == nullptr)
  {
    return false;
  }

  println();
  print0(ucase ? operator_->name : operator_->nameLCase);
  println();

  BOOL_ptr needParen = BOOL::FALSE;
  if ((!right->isParenthesized()) && instanceof <SQLSelectQuery, SQLSelectQueryBlock>(right))
  {
    SQLSelectQueryBlock_ptr rightQuery = std::dynamic_pointer_cast<SQLSelectQueryBlock>(right);
    if (rightQuery->getOrderBy() != nullptr || rightQuery->getLimit() != nullptr)
    {
      needParen = BOOL::TRUE;
    }
  }

  if (needParen)
  {
    print('(');
    right->accept(SharedObject(SQLASTOutputVisitor));
    print(')');
  }
  else
  {
    right->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (orderBy != nullptr)
  {
    println();
    orderBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLLimit_ptr limit = x->getLimit();
  if (limit != nullptr)
  {
    println();
    limit->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (bracket)
  {
    print(')');
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLUnaryExpr_ptr x)
{
  SQLUnaryOperator_ptr operator_ = x->getOperator();
  print0(operator_->name);
  SQLExpr_ptr expr = x->getExpr();

  if (operator_->name->c_str() == SQLUnaryOperator::BINARY.name->c_str() ||
      operator_->name->c_str() == SQLUnaryOperator::Prior.name->c_str() ||
      operator_->name->c_str() == SQLUnaryOperator::ConnectByRoot.name->c_str())
  {
    print(' ');
    if (operator_->name->c_str() != SQLUnaryOperator::Prior.name->c_str() &&
        instanceof <SQLExpr, SQLBinaryOpExpr>(expr))
    {
      print('(');
      expr->accept(SharedObject(SQLASTOutputVisitor));
      print(')');
    }
    else
    {
      expr->accept(SharedObject(SQLASTOutputVisitor));
    }
    return false;
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(expr) ||
      instanceof <SQLExpr, SQLBinaryOpExprGroup>(expr) ||
      instanceof <SQLExpr, SQLUnaryExpr>(expr) ||
      instanceof <SQLExpr, SQLNotExpr>(expr) ||
      instanceof <SQLExpr, SQLBetweenExpr>(expr) ||
      instanceof <SQLExpr, SQLInListExpr>(expr) ||
      instanceof <SQLExpr, SQLInSubQueryExpr>(expr))
  {
    incrementIndent();
    print('(');
    expr->accept(SharedObject(SQLASTOutputVisitor));
    print(')');
    decrementIndent();
  }
  else
  {
    expr->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLHexExpr_ptr x)
{
  if (this->parameterized)
  {
    print('?');
    incrementReplaceCunt();

    if (this->parameters != nullptr)
    {
      ExportParameterVisitorUtils::exportParameter(this->parameters, x);
    }
    return false;
  }

  print0("0x");
  print0(x->getHex());

  uint64_t status = 99;
  Object_ptr charset = x->getAttribute("USING");
  if (charset != nullptr)
  {
    print0(ucase ? " USING " : " using_ ");
    print0(charset->getString());
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSetStatement_ptr x)
{
  if (x->getHeadHintsDirect() != nullptr)
  {
    for (SQLCommentHint_ptr hint : *x->getHeadHintsDirect())
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  if (x->hasBeforeComment())
  {
    printlnComments(x->getBeforeCommentsDirect());
  }

  SQLSetStatement_Option_ptr option_ = x->getOption();

  uint64_t status = 99;
  bool printSet = x->getAttribute("parser.set")->getBool() ||
                      !JdbcUtils::isOracleDbType(dbType);
  if (printSet)
  {
    if (option_->name->c_str() == SQLSetStatement_Option::PROJECT->name->c_str())
    {
      print0(ucase ? "SETPROJECT " : "setproject ");
    }
    else
    {
      print0(ucase ? "SET " : "set ");
    }
  }

  if (option_ != nullptr && option_->name->c_str() != SQLSetStatement_Option::PROJECT->name->c_str())
  {
    print(option_->name);
    print(' ');
  }

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getItems());
  printAndAccept(tmp1, make_string_ptr(", "));

  if (x->getHints() != nullptr && x->getHints()->size() > 0)
  {
    print(' ');
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLCommentHint, SQLObject>(x->getHints());
    printAndAccept(tmp1, make_string_ptr(" "));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAssignItem_ptr x)
{
  x->getTarget()->accept(SharedObject(SQLASTOutputVisitor));

  SQLExpr_ptr value = x->getValue();
  if (value != nullptr)
  {
    print0(" = ");
    value->accept(SharedObject(SQLASTOutputVisitor));
  }
  else
  {
    if (dbType->name->c_str() == DbType::odps.name->c_str())
    {
      print0(" = ");
    }
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCallStatement_ptr x)
{
  if (x->isBrace())
  {
    print('{');
  }
  if (x->getOutParameter() != nullptr)
  {
    x->getOutParameter()->accept(SharedObject(SQLASTOutputVisitor));
    print0(" = ");
  }

  print0(ucase ? "CALL " : "call ");
  x->getProcedureName()->accept(SharedObject(SQLASTOutputVisitor));
  print('(');

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getParameters());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');
  if (x->isBrace())
  {
    print('}');
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLJoinTableSource_ptr x)
{
  SQLCommentHint_ptr hint = x->getHint();
  if (hint != nullptr)
  {
    hint->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLTableSource_ptr left = x->getLeft();

  string_ptr alias = x->getAlias();
  if (alias != nullptr)
  {
    print('(');
  }

  SQLJoinTableSource_JoinType_ptr joinType = x->getJoinType();

  if (instanceof <SQLTableSource, SQLJoinTableSource>(left) &&
    (std::dynamic_pointer_cast<SQLJoinTableSource>(left))->getJoinType()->name->c_str() == SQLJoinTableSource_JoinType::COMMA.name->c_str() &&
    joinType->name->c_str() != SQLJoinTableSource_JoinType::COMMA.name->c_str() &&
    DbType::postgresql.name->c_str() != dbType->name->c_str())
  {
    if ((std::dynamic_pointer_cast<SQLJoinTableSource>(left))->getAttribute("ads.comma_join") == nullptr)
    {
      print('(');
      printTableSource(left);
      print(')');
    }
  }
  else
  {
    printTableSource(left);
  }
  this->indentCount++;

  if (joinType->name->c_str() == SQLJoinTableSource_JoinType::COMMA.name->c_str())
  {
    print(',');
  }
  else
  {
    println();

    BOOL_ptr asof = x->isAsof();
    if (asof)
    {
      print0(ucase ? " ASOF " : " asof ");
    }

    if (x->isNatural())
    {
      print0(ucase ? "NATURAL " : "natural ");
    }

    if (x->isGlobal())
    {
      print0(ucase ? "GLOBAL " : "global ");
    }

    printJoinType(joinType);
  }
  print(' ');

  SQLTableSource_ptr right = x->getRight();
  if (instanceof <SQLTableSource, SQLJoinTableSource>(right) &&
      (std::dynamic_pointer_cast<SQLJoinTableSource>(right))->getJoinType()->name->c_str() == SQLJoinTableSource_JoinType::COMMA.name->c_str())
  {
    print('(');
    printTableSource(right);
    print(')');
  }
  else
  {
    printTableSource(right);
  }

  SQLExpr_ptr condition = x->getCondition();
  if (condition != nullptr)
  {
    BOOL_ptr newLine = BOOL::FALSE;

    if (instanceof <SQLTableSource, SQLSubqueryTableSource>(right))
    {
      newLine = BOOL::TRUE;
    }
    else if (instanceof <SQLExpr, SQLBinaryOpExpr>(condition))
    {
      SQLBinaryOperator_ptr op = (std::dynamic_pointer_cast<SQLBinaryOpExpr>(condition))->getOperator();
      if (op->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str() ||
          op->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str())
      {
        newLine = BOOL::TRUE;
      }
    }
    else if (instanceof <SQLExpr, SQLBinaryOpExprGroup>(condition))
    {
      newLine = BOOL::TRUE;
    }
    if (newLine)
    {
      println();
    }
    else
    {
      print(' ');
    }
    this->indentCount++;
    print0(ucase ? "ON " : "on ");
    printExpr(condition, parameterized);
    this->indentCount--;
  }

  if (x->getUsing()->size() > 0)
  {
    print0(ucase ? " USING (" : " using (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getUsing());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  if (alias != nullptr)
  {
    print0(ucase ? ") AS " : ") as ");
    print0(alias);
  }

  SQLJoinTableSource_UDJ_ptr udj = x->getUdj();
  if (udj != nullptr)
  {
    println();
    udj->accept(SharedObject(SQLASTOutputVisitor));
  }

  this->indentCount--;

  return false;
}

bool SQLASTOutputVisitor::visit(SQLJoinTableSource_UDJ_ptr x)
{
  print0(ucase ? "USING " : "using_ ");
  x->getFunction()->accept(SharedObject(SQLASTOutputVisitor));
  print('(');
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getArguments());
  printAndAccept(tmp1, make_string_ptr(", "));
  print0(") ");
  print0(x->getAlias());
  print0(ucase ? " AS (" : " as (");
  SQLObject_list_ptr tmp2 = ListTran::tran_class<SQLName, SQLObject>(x->getColumns());
  printAndAccept(tmp2, make_string_ptr(", "));
  print0(")");

  SQLAssignItem_list_ptr properties = x->getProperties();
  if (!properties->empty())
  {
    print0(ucase ? " WITH UDFPROPERTIES (" : " with udfproperties (");
    SQLObject_list_ptr tmp3 = ListTran::tran_class<SQLAssignItem, SQLObject>(properties);
    printAndAccept(tmp3, make_string_ptr(", "));
    print(')');
  }
  return false;
}

void SQLASTOutputVisitor::printJoinType(SQLJoinTableSource_JoinType_ptr joinType)
{
  print0(ucase ? joinType->name : joinType->nameLCase);
}

// @Override
bool SQLASTOutputVisitor::visit(SQLInsertStatement_ValuesClause_ptr x)
{
  return visit(x, parameters);
}

bool SQLASTOutputVisitor::visit(SQLInsertStatement_ValuesClause_ptr x, Object_list_ptr parameters)
{
  if ((!this->parameterized) &&
      (*SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputUseInsertValueClauseOriginalString)))) == BOOL::TRUE &&
      x->getOriginalString() != nullptr)
  {
    print0(x->getOriginalString());
    return false;
  }

  int xReplaceCount = x->getReplaceCount();
  SQLExpr_list_ptr values = x->getValues();

  this->replaceCount += xReplaceCount;

  if (xReplaceCount == values->size() && xReplaceCount < variantValuesCache->size())
  {
    string_ptr variantValues;
    if (indentCount == 0)
    {
      variantValues = variantValuesCache_1->at(xReplaceCount);
    }
    else
    {
      variantValues = variantValuesCache->at(xReplaceCount);
    }
    print0(variantValues);
    return false;
  }

  // if (inputParameters != nullptr
  //         && inputParameters->size() > 0){
  //             auto it_inputParameters = inputParameters->begin();
  //             if (inputParameters->get(0) instanceof Collection
  //         && ((Collection) inputParameters->get(0))->size() == x->getValues()->size()) {
  //     incrementIndent();
  //     for (int i = 0; i < inputParameters->size(); i++) {
  //         Collection params = (Collection) inputParameters->get(i);
  //         if (i != 0) {
  //             print(',');
  //             println();
  //         }

  //         print('(');
  //         int j = 0;
  //         for (Object item : (Collection) params) {
  //             if (j != 0) {
  //                 if (j % 5 == 0) {
  //                     println();
  //                 }
  //                 print0(", ");
  //             }
  //             printParameter(item);
  //             j++;
  //         }
  //         print(')');
  //     }
  //     decrementIndent();
  //     return BOOL::FALSE;
  // }

  print('(');
  this->indentCount++;

  auto it_values = values->begin();
  for (int i = 0, size = values->size(); i < size; ++i)
  {
    if (i != 0)
    {
      if (i % 5 == 0)
      {
        println();
      }
      print0(", ");
    }

    std::advance(it_values, i);
    Object_ptr value = *it_values;

    if (instanceof <Object, SQLExpr>(value))
    {
      SQLExpr_ptr expr = std::dynamic_pointer_cast<SQLExpr>(value);

      if (this->parameterized &&
          (instanceof <SQLExpr, SQLIntegerExpr>(expr) || 
          instanceof <SQLExpr, SQLBooleanExpr>(expr) || 
          instanceof <SQLExpr, SQLNumberExpr>(expr) || 
          instanceof <SQLExpr, SQLCharExpr_ptr>(expr) || 
          instanceof <SQLExpr, SQLNCharExpr>(expr) || 
          instanceof <SQLExpr, SQLTimestampExpr>(expr) ||
           instanceof <SQLExpr, SQLDateExpr>(expr) || 
           instanceof <SQLExpr, SQLTimeExpr>(expr)))
      {
        print('?');
        incrementReplaceCunt();
        if (parameters != nullptr)
        {
          ExportParameterVisitorUtils::exportParameter(parameters, expr);
        }
        continue;
      }
      else if (this->parameterized && instanceof <SQLExpr, SQLNullExpr>(expr))
      {
        print('?');
        if (parameters != nullptr)
        {
          parameters->push_back(nullptr);
        }
        incrementReplaceCunt();
      }
      else if (instanceof <SQLExpr, SQLVariantRefExpr>(expr))
      {
        visit(std::dynamic_pointer_cast<SQLVariantRefExpr>(expr));
      }
      else
      {
        expr->accept(SharedObject(SQLASTOutputVisitor));
      }
      // } else if (value instanceof int) {
      //     print(((int) value).intValue());
      // } else if (value instanceof Long) {
      //     print(((Long) value).longValue());
      // } else if (value instanceof string_ptr) {
      //     printChars((string_ptr) value);
      // } else if (value instanceof Float) {
      //     print(((Float) value).floatValue());
      // } else if (value instanceof Double) {
      //     print(((Double) value).doubleValue());
      // } else if (value instanceof time_t) {
      //     print((time_t) value);
      // } else if (value instanceof BigDecimal) {
      //     print(value.toString());
    }
    else if (value == nullptr)
    {
      print0(ucase ? "NULL" : "nullptr");
    }
    else
    {
      throw new UnsupportedOperationException();
    }
  }

  this->indentCount--;
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSomeExpr_ptr x)
{
  print0(ucase ? "SOME (" : "some (");
  this->indentCount++;
  println();
  x->getSubQuery()->accept(SharedObject(SQLASTOutputVisitor));
  this->indentCount--;
  println();
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAnyExpr_ptr x)
{
  print0(ucase ? "ANY (" : "any (");
  this->indentCount++;
  println();
  x->getSubQuery()->accept(SharedObject(SQLASTOutputVisitor));
  this->indentCount--;
  println();
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAllExpr_ptr x)
{
  print0(ucase ? "ALL (" : "all (");
  this->indentCount++;
  println();
  x->getSubQuery()->accept(SharedObject(SQLASTOutputVisitor));
  this->indentCount--;
  println();
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLInSubQueryExpr_ptr x)
{
  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  if (x->isNot())
  {
    if ((*x->isGlobal()) == BOOL::TRUE)
    {
      print0(ucase ? " GLOBAL NOT IN (" : " global not in (");
    }
    else
    {
      print0(ucase ? " NOT IN (" : " not in (");
    }
  }
  else
  {
    if ((*x->isGlobal()) == BOOL::TRUE)
    {
      print0(ucase ? " GLOBAL IN (" : " global in (");
    }
    else
    {
      print0(ucase ? " IN (" : " in (");
    }
  }

  this->indentCount++;
  println();
  x->getSubQuery()->accept(SharedObject(SQLASTOutputVisitor));
  this->indentCount--;
  println();
  print(')');

  if (x->getHint() != nullptr)
  {
    x->getHint()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLListExpr_ptr x)
{
  print('(');
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getItems());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSubqueryTableSource_ptr x)
{
  print('(');
  this->indentCount++;
  println();

  if (x->getHintsSize() > 0)
  {
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLHint, SQLObject>(x->getHints());
    printAndAccept(tmp1, make_string_ptr(" "));
    println();
  }

  this->visit(x->getSelect());
  this->indentCount--;
  println();
  print(')');

  SQLName_list_ptr columns = x->getColumns();
  string_ptr alias = x->getAlias();
  if (alias != nullptr)
  {
    if (columns->size() > 0)
    {
      print0(" AS ");
    }
    else
    {
      print(' ');
    }
    print0(alias);
  }

  if (columns->size() > 0)
  {
    print0(" (");
    auto it_columns = columns->begin();
    for (int i = 0; i < columns->size(); i++)
    {
      if (i != 0)
      {
        print0(", ");
      }
      std::advance(it_columns,i);
      printExpr(*it_columns);
    }
    print(')');
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLUnnestTableSource_ptr x)
{
  print0(ucase ? "UNNEST(" : "unnest(");
  SQLExpr_list_ptr items = x->getItems();
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(items);
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  if (x->isOrdinality())
  {
    print0(ucase ? " WITH ORDINALITY" : " with ordinality");
  }

  SQLName_list_ptr columns = x->getColumns();
  string_ptr alias = x->getAlias();
  if (alias != nullptr)
  {
    if (columns->size() > 0)
    {
      print0(ucase ? " AS " : " as ");
    }
    else
    {
      print(' ');
    }
    print0(alias);
  }

  if (columns->size() > 0)
  {
    print0(" (");
    auto it_columns = columns->begin();
    for (int i = 0; i < columns->size(); i++)
    {
      if (i != 0)
      {
        print0(", ");
      }
      std::advance(it_columns,i);
      printExpr(*it_columns);
    }
    print(')');
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLTruncateStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      visit(std::dynamic_pointer_cast<SQLCommentHint>(hint));
      println();
    }
  }

  print0(ucase ? "TRUNCATE TABLE " : "truncate table ");

  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExprTableSource, SQLObject>(x->getTableSources());
  printAndAccept(tmp1, make_string_ptr(", "));

  if (x->getPartitions()->size() > 0)
  {
    print0(ucase ? " PARTITION (" : " partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartitions());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  if (x->isPartitionAll())
  {
    print0(ucase ? " PARTITION ALL" : " partition all");
  }
  else if (x->getPartitionsForADB()->size() > 0)
  {
    print0(ucase ? " PARTITION " : " partition ");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLIntegerExpr, SQLObject>(x->getPartitionsForADB());
    printAndAccept(tmp1, make_string_ptr(", "));
  }

  if (x->isDropStorage())
  {
    print0(ucase ? " DROP STORAGE" : " drop storage");
  }

  if (x->isReuseStorage())
  {
    print0(ucase ? " REUSE STORAGE" : " reuse storage");
  }

  if (x->isIgnoreDeleteTriggers())
  {
    print0(ucase ? " IGNORE DELETE TRIGGERS" : " ignore delete triggers");
  }

  if (x->isRestrictWhenDeleteTriggers())
  {
    print0(ucase ? " RESTRICT WHEN DELETE TRIGGERS" : " restrict when delete triggers");
  }

  if (x->isContinueIdentity())
  {
    print0(ucase ? " CONTINUE IDENTITY" : " continue identity");
  }

  if (x->isImmediate())
  {
    print0(ucase ? " IMMEDIATE" : " immediate");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDefaultExpr_ptr x)
{
  print0(ucase ? "DEFAULT" : "default");
  return false;
}

// @Override
void SQLASTOutputVisitor::endVisit(SQLCommentStatement_ptr x)
{
}

// @Override
BOOL_ptr SQLASTOutputVisitor::visit(SQLCommentStatement_ptr x)
{
  print0(ucase ? "COMMENT ON " : "comment on ");
  if (x->getType() != nullptr)
  {
    print0(x->getType()->name);
    print(' ');
  }
  x->getOn()->accept(SharedObject(SQLASTOutputVisitor));

  print0(ucase ? " IS " : " is ");
  x->getComment()->accept(SharedObject(SQLASTOutputVisitor));

  return BOOL::FALSE;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLBeginStatement_ptr x)
{
  print0(ucase ? "BEGIN " : "begin ");
  if (x->getTidbTxnMode() != nullptr)
  {
    x->getTidbTxnMode()->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLUseStatement_ptr x)
{
  print0(ucase ? "USE " : "use ");
  x->getDatabase()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

BOOL_ptr SQLASTOutputVisitor::isOdps()
{
  return (DbType::odps.name->c_str() == dbType->name->c_str())?BOOL::TRUE:BOOL::FALSE;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableAddColumn_ptr x)
{
  if (DbType::odps.name->c_str() == dbType->name->c_str() ||
      DbType::hive.name->c_str() == dbType->name->c_str())
  {
    print0(ucase ? "ADD COLUMNS (" : "add columns (");
  }
  else
  {
    print0(ucase ? "ADD (" : "add (");
  }
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLColumnDefinition, SQLObject>(x->getColumns());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  BOOL_ptr restrict = x->getRestrict();
  if (restrict != nullptr &&restrict)
  {
    print0(ucase ? " RESTRICT" : " restrict");
  }

  if (x->isCascade())
  {
    print0(ucase ? " CASCADE" : " cascade");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableReplaceColumn_ptr x)
{
  print0(ucase ? "REPLACE COLUMNS (" : "replace columns (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLColumnDefinition, SQLObject>(x->getColumns());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDropColumnItem_ptr x)
{
  print0(ucase ? "DROP COLUMN " : "drop column ");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getColumns());
  this->printAndAccept(tmp1, make_string_ptr(", "));

  if (x->isCascade())
  {
    print0(ucase ? " CASCADE" : " cascade");
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDeleteByCondition_ptr x)
{
  printUcase(make_string_ptr("DELETE WHERE "));
  x->getWhere()->accept(SharedObject(SQLASTOutputVisitor));

  return false;
}

// @Override
void SQLASTOutputVisitor::endVisit(SQLAlterTableAddColumn_ptr x)
{
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropIndexStatement_ptr x)
{
  print0(ucase ? "DROP INDEX " : "drop index ");
  x->getIndexName()->accept(SharedObject(SQLASTOutputVisitor));

  SQLExprTableSource_ptr table = x->getTableName();
  if (table != nullptr)
  {
    print0(ucase ? " ON " : " on ");
    table->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr algorithm = x->getAlgorithm();
  if (algorithm != nullptr)
  {
    print0(ucase ? " ALGORITHM " : " algorithm ");
    algorithm->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr lockOption = x->getLockOption();
  if (lockOption != nullptr)
  {
    print0(ucase ? " LOCK " : " lock ");
    lockOption->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropLogFileGroupStatement_ptr x)
{
  print0(ucase ? "DROP LOGFILE GROUP " : "drop logfile group ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropServerStatement_ptr x)
{
  print0(ucase ? "DROP SERVER " : "drop server ");
  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropTypeStatement_ptr x)
{
  print0(ucase ? "DROP TYPE " : "drop type ");
  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropSynonymStatement_ptr x)
{
  if (x->isPublic())
  {
    print0(ucase ? "DROP PUBLIC SYNONYM " : "drop synonym ");
  }
  else
  {
    print0(ucase ? "DROP SYNONYM " : "drop synonym ");
  }

  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isForce())
  {
    print0(ucase ? " FORCE" : " force");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSavePointStatement_ptr x)
{
  print0(ucase ? "SAVEPOINT" : "savepoint");
  SQLExpr_ptr name = x->getName();
  if (name != nullptr)
  {
    print(' ');
    name->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLReleaseSavePointStatement_ptr x)
{
  print0(ucase ? "RELEASE SAVEPOINT " : "release savepoint ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLRollbackStatement_ptr x)
{
  print0(ucase ? "ROLLBACK" : "rollback");
  if (x->getTo() != nullptr)
  {
    print0(ucase ? " TO " : " to ");
    x->getTo()->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLCommentHint_ptr x)
{
  if (x->hasBeforeComment() && !parameterized)
  {
    printlnComment(x->getBeforeCommentsDirect());
    println();
  }
  print0("/*");
  print0(x->getText());
  print0("*/");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCreateDatabaseStatement_ptr x)
{
  if (x->getHeadHintsDirect() != nullptr)
  {
    for (SQLCommentHint_ptr hint : *x->getHeadHintsDirect())
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  if (x->hasBeforeComment())
  {
    printlnComments(x->getBeforeCommentsDirect());
  }

  print0(ucase ? "CREATE " : "create ");

  if (x->isPhysical())
  {
    print0(ucase ? "PHYSICAL " : "physical ");
  }

  print0(ucase ? "DATABASE " : "database ");

  if (x->isIfNotExists())
  {
    print0(ucase ? "IF NOT EXISTS " : "if not exists ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getCharacterSet() != nullptr)
  {
    print0(ucase ? " CHARACTER SET " : " character set ");
    print0(x->getCharacterSet());
  }

  string_ptr collate = x->getCollate();
  if (collate != nullptr)
  {
    print0(ucase ? " COLLATE " : " collate ");
    print0(collate);
  }

  SQLExpr_ptr comment = x->getComment();
  if (comment != nullptr)
  {
    print0(ucase ? " COMMENT " : " comment ");
    printExpr(comment, BOOL::FALSE);
  }

  SQLExpr_ptr location = x->getLocation();
  if (location != nullptr)
  {
    print0(ucase ? " LOCATION " : " location ");
    printExpr(location, BOOL::FALSE);
  }

  if (x->getDbProperties()->size() > 0)
  {
    if (dbType->name->c_str() == DbType::mysql.name->c_str() ||
        dbType->name->c_str() == DbType::presto.name->c_str() ||
        dbType->name->c_str() == DbType::trino.name->c_str() ||
        dbType->name->c_str() == DbType::ads.name->c_str() ||
        dbType->name->c_str() == DbType::mariadb.name->c_str())
    {
      println();
      print0(ucase ? "WITH (" : "with (");
    }
    else if (dbType->name->c_str() == DbType::hive.name->c_str())
    {
      println();
      print0(ucase ? "WITH DBPROPERTIES (" : "with dbproperties (");
    }
    else
    {
      println();
      print0(ucase ? "WITH PROPERTIES (" : "with properties (");
    }
    incrementIndent();
    println();
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getDbProperties());
    printlnAndAccept(tmp1, make_string_ptr(","));
    decrementIndent();
    println();
    print(')');
  }

  if (x->getUser() != nullptr)
  {
    print0(ucase ? " FOR " : " for ");
    std::string tmp = "'";
    tmp += x->getUser()->c_str();
    tmp += "'";
    print0(tmp);
  }

  if (x->getOptions()->size() > 0)
  {
    print0(ucase ? " OPTIONS (" : " options (");
    for (auto option : *x->getOptions())
    {
      print0(option.first);
      print0("=");
      printExpr(option.second, BOOL::FALSE);
      print0(" ");
    }
    print(')');
  }

  std::shared_ptr<std::list<SQLAssignItem_list_ptr>> storedBy = x->getStoredBy();
  if (!storedBy->empty())
  {
    println();
    print0(ucase ? "STORED BY " : "stored by ");

    incrementIndent();
    auto it_storedBy = storedBy->begin();
    for (int i = 0; i < storedBy->size(); i++)
    {
      std::advance(it_storedBy, i);
      SQLAssignItem_list_ptr items = *it_storedBy;
      if (i != 0)
      {
        print(',');
        println();
      }
      print('(');
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(items);
      printAndAccept(tmp1, make_string_ptr(", "));
      print(')');
    }
    decrementIndent();
  }

  SQLExpr_ptr storedIn = x->getStoredIn();
  if (storedIn != nullptr)
  {
    println();
    print0(ucase ? "STORED IN " : "stored in ");
    printExpr(storedIn);

    SQLAssignItem_list_ptr storedOn = x->getStoredOn();
    if (storedOn->size() > 0)
    {
      print0(ucase ? " ON (" : " on (");
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(storedOn);
      printAndAccept(tmp1, make_string_ptr(", "));
      print(')');
    }
  }

  SQLExpr_ptr storedAs = x->getStoredAs();
  if (storedAs != nullptr)
  {
    println();
    print0(ucase ? "STORED AS " : "stored as ");
    printExpr(storedAs);
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLCreateTableGroupStatement_ptr x)
{
  print0(ucase ? "CREATE TABLEGROUP " : "create tablegroup ");
  if (x->isIfNotExists())
  {
    print0(ucase ? "IF NOT EXISTS " : "if not exists ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  SQLExpr_ptr partitionNum = x->getPartitionNum();
  if (partitionNum != nullptr)
  {
    print0(ucase ? " PARTITION NUM " : " partition num ");
    printExpr(partitionNum);
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLDropTableGroupStatement_ptr x)
{
  print0(ucase ? "DROP TABLEGROUP " : "drop tablegroup ");
  if (x->isIfExists())
  {
    print0(ucase ? "IF NOT EXISTS " : "if not exists ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableGroupStatement_ptr x)
{
  print0(ucase ? "ALTER TABLEGROUP " : "alter tablegroup ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  print0(" ");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getOptions());
  printAndAccept(tmp1, make_string_ptr(" "));

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterSystemGetConfigStatement_ptr x)
{
  print0(ucase ? "ALTER SYSTEM GET CONFIG " : "alter system get config ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterSystemSetConfigStatement_ptr x)
{
  print0(ucase ? "ALTER SYSTEM SET COFNIG " : "alter system set config ");

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getOptions());
  printAndAccept(tmp1, make_string_ptr(" "));

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterViewStatement_ptr x)
{
  print0(ucase ? "ALTER " : "alter ");

  this->indentCount++;
  string_ptr algorithm = x->getAlgorithm();
  if (algorithm != nullptr && algorithm->length() > 0)
  {
    print0(ucase ? "ALGORITHM = " : "algorithm = ");
    print0(algorithm);
    println();
  }

  SQLName_ptr definer = x->getDefiner();
  if (definer != nullptr)
  {
    print0(ucase ? "DEFINER = " : "definer = ");
    definer->accept(SharedObject(SQLASTOutputVisitor));
    println();
  }

  string_ptr sqlSecurity = x->getSqlSecurity();
  if (sqlSecurity != nullptr && sqlSecurity->length() > 0)
  {
    print0(ucase ? "SQL SECURITY " : "sql security ");
    print0(sqlSecurity);
    println();
  }

  this->indentCount--;

  print0(ucase ? "VIEW " : "view ");

  if (x->isIfNotExists())
  {
    print0(ucase ? "IF NOT EXISTS " : "if not exists ");
  }

  x->getTableSource()->accept(SharedObject(SQLASTOutputVisitor));

  SQLTableElement_list_ptr columns = x->getColumns();
  if (columns->size() > 0)
  {
    print0(" (");
    this->indentCount++;
    println();
    auto it_columns = columns->begin();
    for (int i = 0; i < columns->size(); ++i)
    {
      if (i != 0)
      {
        print0(", ");
        println();
      }
      std::advance(it_columns,i);
      (*it_columns)->accept(SharedObject(SQLASTOutputVisitor));
    }
    this->indentCount--;
    println();
    print(')');
  }

  if (x->getComment() != nullptr)
  {
    println();
    print0(ucase ? "COMMENT " : "comment ");
    x->getComment()->accept(SharedObject(SQLASTOutputVisitor));
  }

  println();
  print0(ucase ? "AS" : "as");
  println();

  x->getSubQuery()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isWithCheckOption())
  {
    println();
    print0(ucase ? "WITH CHECK OPTION" : "with check option");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCreateViewStatement_ptr x)
{
  print0(ucase ? "CREATE " : "create ");
  if (x->isOrReplace())
  {
    print0(ucase ? "OR REPLACE " : "or replace ");
  }

  this->indentCount++;
  string_ptr algorithm = x->getAlgorithm();
  if (algorithm != nullptr && algorithm->length() > 0)
  {
    print0(ucase ? "ALGORITHM = " : "algorithm = ");
    print0(algorithm);
    println();
  }

  SQLName_ptr definer = x->getDefiner();
  if (definer != nullptr)
  {
    print0(ucase ? "DEFINER = " : "definer = ");
    definer->accept(SharedObject(SQLASTOutputVisitor));
    println();
  }

  string_ptr sqlSecurity = x->getSqlSecurity();
  if (sqlSecurity != nullptr && sqlSecurity->length() > 0)
  {
    print0(ucase ? "SQL SECURITY = " : "sql security = ");
    print0(sqlSecurity);
    println();
  }

  this->indentCount--;

  print0(ucase ? "VIEW " : "view ");

  if (x->isIfNotExists())
  {
    print0(ucase ? "IF NOT EXISTS " : "if not exists ");
  }

  x->getTableSource()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isOnCluster())
  {
    print0(ucase ? " ON CLUSTER" : " on cluster");
  }

  SQLName_ptr to = x->getTo();
  if (to != nullptr)
  {
    print0(ucase ? " TO " : " to ");
    to->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLTableElement_list_ptr columns = x->getColumns();
  if (columns->size() > 0)
  {
    print0(" (");
    this->indentCount++;
    println();
    auto it_columns = columns->begin();
    for (int i = 0; i < columns->size(); ++i)
    {
      if (i != 0)
      {
        print0(", ");
        println();
      }
      std::advance(it_columns,i);
      (*it_columns)->accept(SharedObject(SQLASTOutputVisitor));
    }
    this->indentCount--;
    println();
    print(')');
  }

  SQLLiteralExpr_ptr comment = x->getComment();
  if (comment != nullptr)
  {
    println();
    print0(ucase ? "COMMENT " : "comment ");
    comment->accept(SharedObject(SQLASTOutputVisitor));
  }

  println();
  print0(ucase ? "AS" : "as");
  println();

  SQLSelect_ptr subQuery = x->getSubQuery();
  if (subQuery != nullptr)
  {
    subQuery->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLBlockStatement_ptr script = x->getScript();
  if (script != nullptr)
  {
    script->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isWithCheckOption())
  {
    println();
    print0(ucase ? "WITH CHECK OPTION" : "with check option");
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLCreateViewStatement_Column_ptr x)
{
  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));

  SQLCharExpr_ptr comment = x->getComment();
  if (comment != nullptr)
  {
    print0(ucase ? " COMMENT " : " comment ");
    comment->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDropIndex_ptr x)
{
  print0(ucase ? "DROP INDEX " : "drop index ");
  x->getIndexName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLOver_ptr x)
{
  print0(ucase ? "(" : "(");
  if (x->getPartitionBy()->size() > 0)
  {
    print0(ucase ? "PARTITION BY " : "partition by ");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getPartitionBy());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(' ');
  }

  SQLOrderBy_ptr orderBy = x->getOrderBy();
  if (orderBy != nullptr)
  {
    orderBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLOrderBy_ptr distributeBy = x->getDistributeBy();
  if (distributeBy != nullptr)
  {
    SQLSelectOrderByItem_list_ptr items = distributeBy->getItems();

    if (items->size() > 0)
    {
      print0(ucase ? "DISTRIBUTE BY " : "distribute by ");

      auto it_items = items->begin();
      for (int i = 0, size = items->size(); i < size; ++i)
      {
        if (i != 0)
        {
          print0(", ");
        }
        std::advance(it_items,i);
        SQLSelectOrderByItem_ptr item = *it_items;
        visit(item);
      }
    }
  }

  SQLOrderBy_ptr sortBy = x->getSortBy();
  if (sortBy != nullptr)
  {
    SQLSelectOrderByItem_list_ptr items = sortBy->getItems();

    if (items->size() > 0)
    {
      if (distributeBy != nullptr)
      {
        print0(" ");
      }
      print0(ucase ? "SORT BY " : "sort by ");

      auto it_items = items->begin();
      for (int i = 0, size = items->size(); i < size; ++i)
      {
        if (i != 0)
        {
          print0(", ");
        }
        std::advance(it_items,i);
        SQLSelectOrderByItem_ptr item = *it_items;
        visit(item);
      }
    }
  }

  if (x->getOf() != nullptr)
  {
    print0(ucase ? " OF " : " of ");
    x->getOf()->accept(SharedObject(SQLASTOutputVisitor));
  }

  //        if (x->getWindowing() != nullptr) {
  //            if (SQLOver_WindowingType.ROWS.equals(x->getWindowingType())) {
  //                print0(ucase ? " ROWS" : " rows");
  //            } else if (SQLOver_WindowingType.RANGE.equals(x->getWindowingType())) {
  //                print0(ucase ? " RANGE" : " range");
  //            }
  //
  //            printWindowingExpr(x->getWindowing());
  //
  //            if (x->isWindowingPreceding()) {
  //                print0(ucase ? " PRECEDING" : " preceding");
  //            } else if (x->isWindowingFollowing()) {
  //                print0(ucase ? " FOLLOWING" : " following");
  //            }
  //        }

  SQLExpr_ptr windowingBetweenBegin = x->getWindowingBetweenBegin();
  SQLExpr_ptr windowingBetweenEnd = x->getWindowingBetweenEnd();
  SQLOver_WindowingBound_ptr beginBound = x->getWindowingBetweenBeginBound();
  SQLOver_WindowingBound_ptr endBound = x->getWindowingBetweenEndBound();
  SQLOver_WindowingType_ptr windowingType = x->getWindowingType();

  if (windowingBetweenEnd != nullptr || endBound != nullptr)
  {
    if (windowingType != nullptr)
    {
      print(' ');
      print0(ucase ? windowingType->name : windowingType->nameLCase);
    }
    print0(ucase ? " BETWEEN" : " between");

    printWindowingExpr(windowingBetweenBegin);

    if (beginBound != nullptr)
    {
      print(' ');
      print0(ucase ? beginBound->name : beginBound->nameLCase);
    }

    print0(ucase ? " AND" : " and");

    printWindowingExpr(windowingBetweenEnd);

    if (endBound != nullptr)
    {
      print(' ');
      print0(ucase ? endBound->name : endBound->nameLCase);
    }
  }
  else
  {
    if (windowingType != nullptr)
    {
      print(' ');
      print0(ucase ? windowingType->name : windowingType->nameLCase);
    }

    printWindowingExpr(windowingBetweenBegin);

    if (beginBound != nullptr)
    {
      print(' ');
      print0(ucase ? beginBound->name : beginBound->nameLCase);
    }
  }

  if (x->isExcludeCurrentRow())
  {
    print0(ucase ? " EXCLUDE CURRENT ROW" : " exclude current row");
  }

  print(')');
  return false;
}

void SQLASTOutputVisitor::printWindowingExpr(SQLExpr_ptr expr)
{
  if (expr == nullptr)
  {
    return;
  }
  print(' ');
  if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
  {
    string_ptr ident = (std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->getName();
    print0(ucase ? ident : StringUtils::toUpper(ident));
  }
  else
  {
    expr->accept(SharedObject(SQLASTOutputVisitor));
  }
}

// @Override
bool SQLASTOutputVisitor::visit(SQLKeep_ptr x)
{
  if (x->getDenseRank() == SQLKeep_DenseRank::FIRST)
  {
    print0(ucase ? "KEEP (DENSE_RANK FIRST " : "keep (dense_rank first ");
  }
  else
  {
    print0(ucase ? "KEEP (DENSE_RANK LAST " : "keep (dense_rank last ");
  }

  x->getOrderBy()->accept(SharedObject(SQLASTOutputVisitor));
  print(')');

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLColumnPrimaryKey_ptr x)
{
  SQLName_ptr name = x->getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }
  print0(ucase ? "PRIMARY KEY" : "primary key");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLColumnUniqueKey_ptr x)
{
  SQLName_ptr name = x->getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }
  print0(ucase ? "UNIQUE" : "unique");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLColumnCheck_ptr x)
{
  SQLName_ptr name = x->getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }
  print0(ucase ? "CHECK (" : "check (");
  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  print(')');

  BOOL_ptr enable = x->getEnable();
  if (enable != nullptr)
  {
    if (enable)
    {
      print0(ucase ? " ENABLE" : " enable");
    }
    else
    {
      print0(ucase ? " DISABLE" : " disable");
    }
  }

  BOOL_ptr enforced = x->getEnforced();
  if (enforced != nullptr)
  {
    if (enforced)
    {
      print0(ucase ? " ENFORCED" : " enforced");
    }
    else
    {
      print0(ucase ? " NOT ENFORCED" : " not enforced");
    }
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLWithSubqueryClause_ptr x)
{
  if ((!isParameterized()) && isPrettyFormat() && x->hasBeforeComment())
  {
    printlnComments(x->getBeforeCommentsDirect());
  }

  print0(ucase ? "WITH " : "with ");
  if (x->getRecursive() == BOOL::TRUE)
  {
    print0(ucase ? "RECURSIVE " : "recursive ");
  }
  this->indentCount++;
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLWithSubqueryClause_Entry, SQLObject>(x->getEntries());
  printlnAndAccept(tmp1, make_string_ptr(", "));
  this->indentCount--;
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLWithSubqueryClause_Entry_ptr x)
{
  if (isPrettyFormat() && x->hasBeforeComment())
  {
    printlnComments(x->getBeforeCommentsDirect());
  }

  print0(x->getAlias());

  if (x->getColumns()->size() > 0)
  {
    print0(" (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getColumns());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }
  print(' ');
  print0(ucase ? "AS " : "as ");
  print('(');
  this->indentCount++;
  println();
  SQLSelect_ptr query = x->getSubQuery();
  if (query != nullptr)
  {
    query->accept(SharedObject(SQLASTOutputVisitor));
  }
  else
  {
    x->getReturningStatement()->accept(SharedObject(SQLASTOutputVisitor));
  }
  this->indentCount--;
  println();
  print(')');

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableModifyClusteredBy_ptr x)
{
  print0(ucase ? "CLUSTERED BY " : "clustered by ");
  if (!x->getClusterColumns()->empty())
  {
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getClusterColumns());
    printAndAccept(tmp1, make_string_ptr(", "));
  }
  else
  {
    print0("()");
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableAlterColumn_ptr x)
{
  if (DbType::odps.name->c_str() == dbType->name->c_str())
  {
    print0(ucase ? "CHANGE COLUMN " : "change column ");
  }
  else if (DbType::hive.name->c_str() == dbType->name->c_str())
  {
    print0(ucase ? "CHANGE " : "change ");
  }
  else
  {
    print0(ucase ? "ALTER COLUMN " : "alter column ");
  }

  SQLName_ptr originColumn = x->getOriginColumn();
  if (originColumn != nullptr)
  {
    originColumn->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }

  x->getColumn()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isSetNotNull())
  { // postgresql
    print0(ucase ? " SET NOT NULL" : " set not nullptr");
  }

  if (x->isDropNotNull())
  { // postgresql
    print0(ucase ? " DROP NOT NULL" : " drop not nullptr");
  }

  if (x->getSetDefault() != nullptr)
  { // postgresql
    print0(ucase ? " SET DEFAULT " : " set default ");
    x->getSetDefault()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isDropDefault())
  { // postgresql
    print0(ucase ? " DROP DEFAULT" : " drop default");
  }

  SQLDataType_ptr dataType = x->getDataType();
  if (dataType != nullptr)
  {
    print0(ucase ? " SET DATA TYPE " : " set data type ");
    dataType->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLName_ptr after = x->getAfter();
  if (after != nullptr)
  {
    print0(ucase ? " AFTER " : " after ");
    after->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isFirst())
  {
    print0(ucase ? " FIRST" : " first");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCheck_ptr x)
{
  SQLName_ptr name = x->SQLConstraintImpl::getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }

  print0(ucase ? "CHECK (" : "check (");
  this->indentCount++;
  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  this->indentCount--;
  print(')');

  BOOL_ptr enforced = x->getEnforced();
  if (enforced != nullptr)
  {
    if (enforced)
    {
      print0(ucase ? " ENFORCED" : " enforced");
    }
    else
    {
      print0(ucase ? " NOT ENFORCED" : " not enforced");
    }
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDefault_ptr x)
{
  SQLName_ptr name = x->SQLConstraintImpl::getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }

  print0(ucase ? "DEFAULT " : "default ");
  this->indentCount++;
  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  this->indentCount--;

  print0(ucase ? " FOR " : " for ");

  this->indentCount++;
  x->getColumn()->accept(SharedObject(SQLASTOutputVisitor));
  this->indentCount--;

  if (x->isWithValues())
  {
    print0(ucase ? " WITH VALUES" : " with values");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDropForeignKey_ptr x)
{
  print0(ucase ? "DROP FOREIGN KEY " : "drop foreign key ");
  x->getIndexName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDropPrimaryKey_ptr x)
{
  print0(ucase ? "DROP PRIMARY KEY" : "drop primary key");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDropKey_ptr x)
{
  print0(ucase ? "DROP KEY " : "drop key ");
  x->getKeyName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTablePartitionCount_ptr x)
{
  print0(ucase ? "PARTITIONS " : "partitons ");
  x->getCount()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableBlockSize_ptr x)
{
  print0(ucase ? "BLOCK_SIZE " : "block_size ");
  x->getSize()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableEnableKeys_ptr x)
{
  print0(ucase ? "ENABLE KEYS" : "enable keys");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDisableKeys_ptr x)
{
  print0(ucase ? "DISABLE KEYS" : "disable keys");
  return false;
}

bool SQLASTOutputVisitor::visit(SQLAlterTableDisableConstraint_ptr x)
{
  print0(ucase ? "DISABLE CONSTRAINT " : "disable constraint ");
  x->getConstraintName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLAlterTableEnableConstraint_ptr x)
{
  print0(ucase ? "ENABLE CONSTRAINT " : "enable constraint ");
  x->getConstraintName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDropConstraint_ptr x)
{
  print0(ucase ? "DROP CONSTRAINT " : "drop constraint ");
  x->getConstraintName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isCascade())
  {
    print0(ucase ? " CASCADE" : " cascade");
  }
  else if (x->isRestrict())
  {
    print0(ucase ? " RESTRICT" : " restrict");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableStatement_ptr x)
{
  print0(ucase ? "ALTER TABLE " : "alter table ");

  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }

  printTableSourceExpr(x->getName());

  SQLName_ptr on = x->getOn();
  if (on != nullptr)
  {
    print(ucase ? " ON CLUSTER " : " on cluster ");
    printExpr(on);
  }

  this->indentCount++;
  auto it_getItems = x->getItems()->begin();
  for (int i = 0; i < x->getItems()->size(); ++i)
  {
    std::advance(it_getItems, i);
    SQLAlterTableItem_ptr item = *it_getItems;
    if (i != 0)
    {
      std::advance(it_getItems, i - 1);
      SQLAlterTableItem_ptr former = *it_getItems;
      if (this->dbType->name->c_str() == DbType::hive.name->c_str() && 
      instanceof <SQLAlterTableItem, SQLAlterTableAddPartition>(former) && 
      instanceof <SQLAlterTableItem, SQLAlterTableAddPartition>(item))
      {
        // ignore comma
      }
      else
      {
        print(',');
      }
    }
    println();
    item->accept(SharedObject(SQLASTOutputVisitor));
  }
  this->indentCount--;

  if (x->isMergeSmallFiles())
  {
    print0(ucase ? " MERGE SMALLFILES" : " merge smallfiles");
  }

  SQLSelectOrderByItem_list_ptr clusteredBy = x->getClusteredBy();
  if (clusteredBy->size() > 0)
  {
    println();
    if (x->isRange())
    {
      print0(ucase ? "RANGE " : "range ");
    }
    print0(ucase ? "CLUSTERED BY (" : "clustered by (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(clusteredBy);
    printAndAccept(tmp1, make_string_ptr(","));
    print0(")");
  }

  SQLSelectOrderByItem_list_ptr sortedBy = x->getSortedBy();
  if (sortedBy->size() > 0)
  {
    println();
    print0(ucase ? "SORTED BY (" : "sorted by (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(sortedBy);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  int buckets = x->getBuckets();
  if (buckets > 0)
  {
    println();
    print0(ucase ? "INTO " : "into ");
    print(buckets);
    print0(ucase ? " BUCKETS" : " buckets");
  }

  int shards = x->getShards();
  if (shards > 0)
  {
    println();
    print0(ucase ? "INTO " : "into ");
    print(shards);
    print0(ucase ? " SHARDS" : " shards");
  }

  if (x->isNotClustered())
  {
    print0(ucase ? " NOT CLUSTERED" : " not clustered");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLExprHint_ptr x)
{
  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCreateIndexStatement_ptr x)
{
  print0(ucase ? "CREATE " : "create ");

  string_ptr type = x->getType();

  if (type != nullptr)
  {
    print0(ucase ? StringUtils::toUpper( type) : StringUtils::toLower(type));
    print(' ');
  }

  if (x->isGlobal())
  {
    print0(ucase ? "GLOBAL " : "global ");
  }

  if (x->isLocal())
  {
    print0(ucase ? "LOCAL " : "local ");
  }

  print0(ucase ? "INDEX " : "index ");

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " ON " : " on ");
  x->getTable()->accept(SharedObject(SQLASTOutputVisitor));
  print0(" (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(x->getItems());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  SQLName_list_ptr covering = x->getCovering();
  if (covering->size() > 0)
  {
    print0(ucase ? " COVERING (" : " covering (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(covering);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  SQLExpr_ptr dbPartitionBy = x->getDbPartitionBy();
  if (dbPartitionBy != nullptr)
  {
    print0(ucase ? " DBPARTITION BY " : " dbpartition by ");
    dbPartitionBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr tablePartitionBy = x->getTablePartitionBy();
  if (tablePartitionBy != nullptr)
  {
    print0(ucase ? " TBPARTITION BY " : " tbpartition by ");
    tablePartitionBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr tablePartitions = x->getTablePartitions();
  if (tablePartitions != nullptr)
  {
    print0(ucase ? " TBPARTITIONS " : " tbpartitions ");
    tablePartitions->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getIndexDefinition()->hasOptions())
  {
    string_ptr using_ = x->getIndexDefinition()->getOptions()->getIndexType();
    if (using_ != nullptr)
    {
      print0(ucase ? " USING " : " using ");
      print0(ucase ? StringUtils::toUpper( using_) : StringUtils::toLower(using_));
    }

    x->getIndexDefinition()->getOptions()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLUnique_ptr x)
{
  SQLName_ptr name = x->getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }
  print0(ucase ? "UNIQUE (" : "unique (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(x->getColumns());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPrimaryKeyImpl_ptr x)
{
  SQLName_ptr name = x->getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }

  print0(ucase ? "PRIMARY KEY " : "primary key ");

  if (x->isClustered())
  {
    print0(ucase ? "CLUSTERED " : "clustered ");
  }

  print('(');
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(x->getColumns());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  if (x->isDisableNovalidate())
  {
    print0(ucase ? " DISABLE NOVALIDATE" : " disable novalidate");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableRenameColumn_ptr x)
{
  print0(ucase ? "RENAME COLUMN " : "rename column ");
  x->getColumn()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " TO " : " to ");
  x->getTo()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableRenameConstraint_ptr x)
{
  print0(ucase ? "RENAME CONSTRAINT " : "rename constraint ");
  x->getConstraint()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " TO " : " to ");
  x->getTo()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLColumnReference_ptr x)
{
  SQLName_ptr name = x->getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }

  print0(ucase ? "REFERENCES " : "references ");
  x->getTable()->accept(SharedObject(SQLASTOutputVisitor));
  print0(" (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getColumns());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  SQLForeignKeyImpl_Match_ptr match = x->getReferenceMatch();
  if (match != nullptr)
  {
    print0(ucase ? " MATCH " : " match ");
    print0(ucase ? match->name : match->nameLCase);
  }

  if (x->getOnDelete() != nullptr)
  {
    print0(ucase ? " ON DELETE " : " on delete ");
    print0(ucase ? x->getOnDelete()->name : x->getOnDelete()->nameLCase);
  }

  if (x->getOnUpdate() != nullptr)
  {
    print0(ucase ? " ON UPDATE " : " on update ");
    print0(ucase ? x->getOnUpdate()->name : x->getOnUpdate()->nameLCase);
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLForeignKeyImpl_ptr x)
{
  SQLName_ptr name = x->getName();
  if (name != nullptr)
  {
    print0(ucase ? "CONSTRAINT " : "constraint ");
    name->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }

  print0(ucase ? "FOREIGN KEY (" : "foreign key (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getReferencingColumns());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  this->indentCount++;
  println();
  print0(ucase ? "REFERENCES " : "references ");
  x->getReferencedTableName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getReferencedColumns()->size() > 0)
  {
    print0(" (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getReferencedColumns());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  if (x->isOnDeleteCascade())
  {
    println();
    print0(ucase ? "ON DELETE CASCADE" : "on delete cascade");
  }
  else if (x->isOnDeleteSetNull())
  {
    print0(ucase ? "ON DELETE SET NULL" : "on delete set nullptr");
  }

  if (x->isDisableNovalidate())
  {
    print0(ucase ? " DISABLE NOVALIDATE" : " disable novalidate");
  }

  this->indentCount--;
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropSequenceStatement_ptr x)
{
  print0(ucase ? "DROP SEQUENCE " : "drop sequence ");
  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
void SQLASTOutputVisitor::endVisit(SQLDropSequenceStatement_ptr x)
{
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropTriggerStatement_ptr x)
{
  print0(ucase ? "DROP TRIGGER " : "drop trigger ");
  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
void SQLASTOutputVisitor::endVisit(SQLDropUserStatement_ptr x)
{
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropUserStatement_ptr x)
{
  print0(ucase ? "DROP USER " : "drop user ");
  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getUsers());
  printAndAccept(tmp1, make_string_ptr(", "));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCancelJobStatement_ptr x)
{
  print0(ucase ? "CANCEL JOB " : "cancel job ");
  x->getJobName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLExplainAnalyzeStatement_ptr x)
{
  print0(ucase ? "EXPLAIN ANALYZE " : "explain analyze ");
  println();
  x->getSelect()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLExplainStatement_ptr x)
{
  print0(ucase ? "EXPLAIN" : "explain");
  if (x->getHints() != nullptr && x->getHints()->size() > 0)
  {
    print(' ');
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLCommentHint, SQLObject>(x->getHints());
    printAndAccept(tmp1, make_string_ptr(" "));
  }

  if (x->isExtended())
  {
    print0(ucase ? " EXTENDED" : " extended");
  }

  if (x->isDependency())
  {
    print0(ucase ? " DEPENDENCY" : " dependency");
  }

  if (x->isAuthorization())
  {
    print0(ucase ? " AUTHORIZATION" : " authorization");
  }

  string_ptr type = x->getType();
  if (type != nullptr)
  {
    print(' ');
    if (type->find(' ') != std::string::npos)
    {
      print('(');
      print0(type);
      print(')');
    }
    else
    {
      print0(type);
    }
  }
  println();
  x->getStatement()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

void SQLASTOutputVisitor::printGrantPrivileges(SQLGrantStatement_ptr x)
{
}

// @Override
bool SQLASTOutputVisitor::visit(SQLGrantStatement_ptr x)
{
  print0(ucase ? "GRANT " : "grant ");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLPrivilegeItem, SQLObject>(x->getPrivileges());
  printAndAccept(tmp1, make_string_ptr(", "));

  printGrantOn(x);

  if (x->getUsers() != nullptr)
  {
    print0(ucase ? " TO " : " to ");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getUsers());
    printAndAccept(tmp1, make_string_ptr(","));
  }

  if (x->getWithGrantOption())
  {
    print0(ucase ? " WITH GRANT OPTION" : " with grant option");
  }

  BOOL_ptr with = BOOL::FALSE;
  SQLExpr_ptr maxQueriesPerHour = x->getMaxQueriesPerHour();
  if (maxQueriesPerHour != nullptr)
  {
    if (!with)
    {
      print0(ucase ? " WITH" : " with");
      with = BOOL::TRUE;
    }
    print0(ucase ? " MAX_QUERIES_PER_HOUR " : " max_queries_per_hour ");
    maxQueriesPerHour->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr maxUpdatesPerHour = x->getMaxUpdatesPerHour();
  if (maxUpdatesPerHour != nullptr)
  {
    if (!with)
    {
      print0(ucase ? " WITH" : " with");
      with = BOOL::TRUE;
    }
    print0(ucase ? " MAX_UPDATES_PER_HOUR " : " max_updates_per_hour ");
    maxUpdatesPerHour->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr maxConnectionsPerHour = x->getMaxConnectionsPerHour();
  if (maxConnectionsPerHour != nullptr)
  {
    if (!with)
    {
      print0(ucase ? " WITH" : " with");
      with = BOOL::TRUE;
    }
    print0(ucase ? " MAX_CONNECTIONS_PER_HOUR " : " max_connections_per_hour ");
    maxConnectionsPerHour->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr maxUserConnections = x->getMaxUserConnections();
  if (maxUserConnections != nullptr)
  {
    if (!with)
    {
      print0(ucase ? " WITH" : " with");
      with = BOOL::TRUE;
    }
    print0(ucase ? " MAX_USER_CONNECTIONS " : " max_user_connections ");
    maxUserConnections->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isAdminOption())
  {
    if (!with)
    {
      print0(ucase ? " WITH" : " with");
      with = BOOL::TRUE;
    }
    print0(ucase ? " ADMIN OPTION" : " admin option");
  }

  if (x->getIdentifiedBy() != nullptr)
  {
    print0(ucase ? " IDENTIFIED BY " : " identified by ");
    x->getIdentifiedBy()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

void SQLASTOutputVisitor::printGrantOn(SQLGrantStatement_ptr x)
{
  if (x->getResource() != nullptr)
  {
    print0(ucase ? " ON " : " on ");

    SQLObjectType_ptr resourceType = x->getResourceType();
    if (resourceType != nullptr)
    {
      print0(ucase ? resourceType->name : resourceType->nameLCase);
      print(' ');
    }

    x->getResource()->accept(SharedObject(SQLASTOutputVisitor));
  }
}

// @Override
bool SQLASTOutputVisitor::visit(SQLRevokeStatement_ptr x)
{
  print0(ucase ? "REVOKE " : "revoke ");

  if (x->isGrantOption())
  {
    print0(ucase ? "GRANT OPTION" : "grant option");

    if (x->getPrivileges()->size() > 0)
    {
      print0(ucase ? " FOR " : " for ");
    }
  }

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLPrivilegeItem, SQLObject>(x->getPrivileges());
  printAndAccept(tmp1, make_string_ptr(", "));

  if (x->getResource() != nullptr)
  {
    print0(ucase ? " ON " : " on ");

    if (x->getResourceType() != nullptr)
    {
      print0(x->getResourceType()->name);
      print(' ');
    }

    x->getResource()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getUsers() != nullptr)
  {
    print0(ucase ? " FROM " : " from ");

    if (dbType->name->c_str() == DbType::odps.name->c_str())
    {
      print0(ucase ? "USER " : "user ");
    }

    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getUsers());
    printAndAccept(tmp1, make_string_ptr(", "));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropDatabaseStatement_ptr x)
{
  print0(ucase ? "DROP " : "drop ");

  if (x->isPhysical())
  {
    print0(ucase ? "PHYSICAL " : "physical ");
  }

  print0(ucase ? "DATABASE " : "database ");

  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }

  x->getDatabase()->accept(SharedObject(SQLASTOutputVisitor));

  BOOL_ptr restrict = x->getRestrict();
  if (restrict != nullptr &&restrict)
  {
    print0(ucase ? " RESTRICT" : " restrict");
  }

  if (x->isCascade())
  {
    print0(ucase ? " CASCADE" : " cascade");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropCatalogStatement_ptr x)
{
  if (x->isExternal())
  {
    print0(ucase ? "DROP EXTERNAL CATALOG " : "drop external catalog ");
  }
  else
  {
    print0(ucase ? "DROP CATALOG " : "drop catalog ");
  }

  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropFunctionStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      visit(std::dynamic_pointer_cast<SQLCommentHint>(hint));
      println();
    }
  }

  if (x->hasBeforeComment())
  {
    printlnComments(x->getBeforeCommentsDirect());
  }

  if (x->isTemporary())
  {
    print0(ucase ? "DROP TEMPORARY FUNCTION " : "drop temporary function ");
  }
  else
  {
    print0(ucase ? "DROP FUNCTION " : "drop function ");
  }

  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropTableSpaceStatement_ptr x)
{
  print0(ucase ? "DROP TABLESPACE " : "drop tablespace ");

  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  SQLExpr_ptr engine = x->getEngine();
  if (engine != nullptr)
  {
    print0(ucase ? " ENGINE " : " engine ");
    engine->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropProcedureStatement_ptr x)
{
  print0(ucase ? "DROP PROCEDURE " : "drop procedure ");

  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLIndexOptions_ptr x)
{
  /*
  string_ptr using_ = x->getIndexType();
  if (using_ != nullptr) {
      print0(ucase ? " USING " : " using_ ");
      print0(using_);
  }
  */
  if (x->isGlobal())
  {
    print0(ucase ? " GLOBAL" : " global");
  }
  else if (x->isLocal())
  {
    print0(ucase ? " LOCAL" : " local");
  }

  if (x->isInvisible())
  {
    print0(ucase ? " INVISIBLE" : " invisible");
  }
  else if (x->isVisible())
  {
    print0(ucase ? " VISIBLE" : " visible");
  }

  SQLExpr_ptr keyBlockSize = x->getKeyBlockSize();
  if (keyBlockSize != nullptr)
  {
    print0(ucase ? " KEY_BLOCK_SIZE = " : " key_block_size = ");
    printExpr(keyBlockSize, parameterized);
  }

  string_ptr parserName = x->getParserName();
  if (parserName != nullptr)
  {
    print0(ucase ? " WITH PARSER " : " with parser ");
    print0(parserName);
  }

  SQLExpr_ptr comment = x->getComment();
  if (comment != nullptr)
  {
    print0(ucase ? " COMMENT " : " comment ");
    printExpr(comment, parameterized);
  }

  string_ptr algorithm = x->getAlgorithm();
  if (algorithm != nullptr)
  {
    print0(ucase ? " ALGORITHM = " : " algorithm = ");
    print0(algorithm);
  }

  string_ptr lock = x->getLock();
  if (lock != nullptr)
  {
    print0(ucase ? " LOCK = " : " lock = ");
    print0(lock);
  }

  SQLAssignItem_list_ptr options = x->getOtherOptions();
  if (options->size() > 0)
  {
    for (SQLAssignItem_ptr option : *options)
    {
      print(' ');
      option->accept(SharedObject(SQLASTOutputVisitor));
    }
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLIndexDefinition_ptr x)
{
  /*
  if (x->hasConstraint()) {
      print0(ucase ? "CONSTRAINT " : "constraint ");
      if (x->getSymbol() != nullptr) {
          x->getSymbol()->accept(SharedObject(SQLASTOutputVisitor));
          print(' ');
      }
  }
  */

  BOOL_ptr mysql = (DbType::mysql.name->c_str() == dbType->name->c_str())?BOOL::TRUE:BOOL::FALSE;

  string_ptr type = x->getType();

  if (type != nullptr)
  {
    print0(ucase ? StringUtils::toUpper( type) : StringUtils::toLower( type));
    print(' ');
  }

  if (x->isGlobal())
  {
    print0(ucase ? "GLOBAL " : "global ");
  }
  else if (x->isLocal())
  {
    print0(ucase ? "LOCAL " : "local ");
  }

  if (x->isIndex())
  {
    print0(ucase ? "INDEX " : "index ");
  }

  if (x->isKey())
  {
    print0(ucase ? "KEY " : "key ");
  }

  if (x->getName() != nullptr &&
      (type == nullptr ||
       !StringUtils::equalsIgnoreCase(type, make_string_ptr("primary"))))
  {
    x->getName()->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }

  string_ptr using_ = x->getOptions()->getIndexType();
  if (using_ != nullptr)
  {
    print0(ucase ? "USING " : "using_ ");
    print0(using_);
    print(' ');
  }

  if (x->isHashMapType())
  {
    print0(ucase ? "HASHMAP " : "hashmap ");
  }

  if (x->getColumns()->size() > 0)
  {
    print('(');
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(x->getColumns());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  if (x->getAnalyzerName() != nullptr)
  {
    print0(ucase ? " WITH ANALYZER " : " with analyzer ");
    x->getAnalyzerName()->accept(SharedObject(SQLASTOutputVisitor));
  }
  else
  {
    if (x->getIndexAnalyzerName() != nullptr)
    {
      print0(ucase ? " WITH INDEX ANALYZER " : " with index analyzer ");
      x->getIndexAnalyzerName()->accept(SharedObject(SQLASTOutputVisitor));
    }

    if (x->getQueryAnalyzerName() != nullptr)
    {
      print0(ucase ? " WITH QUERY ANALYZER " : " with query analyzer ");
      x->getQueryAnalyzerName()->accept(SharedObject(SQLASTOutputVisitor));
    }

    if (x->getWithDicName() != nullptr)
    {
      printUcase(make_string_ptr(" WITH DICT "));
      x->getWithDicName()->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  SQLName_list_ptr covering = x->getCovering();
  if (covering->size() > 0)
  {
    print0(ucase ? " COVERING (" : " covering (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(covering);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  SQLExpr_ptr dbPartitionBy = x->getDbPartitionBy();
  if (dbPartitionBy != nullptr)
  {
    print0(ucase ? " DBPARTITION BY " : " dbpartition by ");
    dbPartitionBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr tablePartitionBy = x->getTbPartitionBy();
  if (tablePartitionBy != nullptr)
  {
    print0(ucase ? " TBPARTITION BY " : " tbpartition by ");
    tablePartitionBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr tablePartitions = x->getTbPartitions();
  if (tablePartitions != nullptr)
  {
    print0(ucase ? " TBPARTITIONS " : " tbpartitions ");
    tablePartitions->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->hasOptions())
  {
    x->getOptions()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableAddIndex_ptr x)
{
  print0(ucase ? "ADD " : "add ");

  visit(x->getIndexDefinition());

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableAlterIndex_ptr x)
{
  print0(ucase ? "ALTER " : "alter ");

  visit(x->getIndexDefinition());

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableAddConstraint_ptr x)
{
  if (x->isWithNoCheck())
  {
    print0(ucase ? "WITH NOCHECK " : "with nocheck ");
  }

  print0(ucase ? "ADD " : "add ");

  x->getConstraint()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLCreateTriggerStatement_ptr x)
{
  print0(ucase ? "CREATE " : "create ");

  if ((*x->isOrReplace())==BOOL::TRUE)
  {
    print0(ucase ? "OR REPLACE " : "or replace ");
  }

  print0(ucase ? "TRIGGER " : "trigger ");

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  this->indentCount++;
  println();
  if (SQLCreateTriggerStatement_TriggerType::INSTEAD_OF->name->c_str() == x->getTriggerType()->name->c_str())
  {
    print0(ucase ? "INSTEAD OF" : "instead of");
  }
  else
  {
    string_ptr triggerTypeName = x->getTriggerType()->name;
    print0(ucase ? triggerTypeName : StringUtils::toLower(triggerTypeName));
  }

  if ((*x->isInsert()) == BOOL::TRUE)
  {
    print0(ucase ? " INSERT" : " insert");
  }

  if (x->isDelete())
  {
    if ((*x->isInsert()) == BOOL::TRUE)
    {
      print0(ucase ? " OR" : " or");
    }
    print0(ucase ? " DELETE" : " delete");
  }

  if ((*x->isUpdate()) == BOOL::TRUE)
  {
    if ((*x->isInsert()) == BOOL::TRUE || (*x->isDelete()) == BOOL::TRUE)
    {
      print0(ucase ? " OR" : " or");
    }
    print0(ucase ? " UPDATE" : " update");

    SQLName_list_ptr colums = x->getUpdateOfColumns();
    for (SQLName_ptr colum : *colums)
    {
      print(' ');
      colum->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  println();
  print0(ucase ? "ON " : "on ");
  x->getOn()->accept(SharedObject(SQLASTOutputVisitor));

  if ((*x->isForEachRow()) == BOOL::TRUE)
  {
    println();
    print0(ucase ? "FOR EACH ROW" : "for each row");
  }

  SQLExpr_ptr when = x->getWhen();
  if (when != nullptr)
  {
    println();
    print0(ucase ? "WHEN " : "when ");
    when->accept(SharedObject(SQLASTOutputVisitor));
  }
  this->indentCount--;
  println();
  x->getBody()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLBooleanExpr_ptr x)
{
  if (isParameterized())
  {
    print('?');
    return false;
  }
  print0(x->getBooleanValue() ? "BOOL::TRUE" : "BOOL::FALSE");
  return false;
}

void SQLASTOutputVisitor::endVisit(SQLBooleanExpr_ptr x)
{
}

// @Override
bool SQLASTOutputVisitor::visit(SQLUnionQueryTableSource_ptr x)
{
  print('(');
  this->indentCount++;
  println();
  x->getUnion()->accept(SharedObject(SQLASTOutputVisitor));
  this->indentCount--;
  println();
  print(')');

  SQLName_list_ptr columns = x->getColumns();
  string_ptr alias = x->getAlias();
  if (alias != nullptr)
  {
    if (columns->size() > 0)
    {
      print0(" AS ");
    }
    else
    {
      print(' ');
    }
    print0(alias);
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLTimestampExpr_ptr x)
{
  if (this->parameterized)
  {
    print('?');
    incrementReplaceCunt();

    if (this->parameters != nullptr)
    {
      ExportParameterVisitorUtils::exportParameter(this->parameters, x);
    }
  }
  else
  {
    print0(ucase ? "TIMESTAMP " : "timestamp ");

    if (x->isWithTimeZone())
    {
      print0(ucase ? " WITH TIME ZONE " : " with time zone ");
    }

    print('\'');
    print0(x->getLiteral());
    print('\'');

    if (x->getTimeZone() != nullptr)
    {
      print0(ucase ? " AT TIME ZONE '" : " at time zone '");
      print0(x->getTimeZone());
      print('\'');
    }
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLBinaryExpr_ptr x)
{
  print0("b'");
  print0(x->getText());
  print('\'');

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableRename_ptr x)
{
  print0(ucase ? "RENAME TO " : "rename to ");
  x->getTo()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowTablesStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
      println();
    }
  }

  if (x->isFull())
  {
    print0(ucase ? "SHOW FULL TABLES" : "show full tables");
  }
  else
  {
    print0(ucase ? "SHOW TABLES" : "show tables");
  }

  if (x->isExtended())
  {
    print0(ucase ? " EXTENDED" : " extended");
  }

  if (x->getDatabase() != nullptr)
  {
    print0(ucase ? " FROM " : " from ");
    x->getDatabase()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getLike() != nullptr)
  {
    print0(ucase ? " LIKE " : " like ");
    x->getLike()->accept(SharedObject(SQLASTOutputVisitor));
  }
  else if (x->getWhere() != nullptr)
  {
    print0(ucase ? " WHERE " : " where ");
    x->getWhere()->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

void SQLASTOutputVisitor::printlnComment(string_list_ptr comments)
{
  if (comments != nullptr)
  {
    auto it_comments = comments->begin();
    for (int i = 0; i < comments->size(); ++i)
    {
      std::advance(it_comments, i);
      string_ptr comment =  * it_comments;
      if (i != 0 &&
          StringUtils::startWith(comment, make_string_ptr("--")))
      {
        println();
      }

      printComment(comment);
    }
  }
}

void SQLASTOutputVisitor::printComment(string_ptr comment)
{
  if (comment == nullptr)
  {
    return;
  }

  if (StringUtils::startWith(comment, make_string_ptr("--")) && comment->length() > 2 && comment->at(2) != ' ' && comment->at(2) != '-')
  {
    print0("-- ");
    print0(comment->substr(2));
  }
  else
  {
    print0(comment);
  }
}

void SQLASTOutputVisitor::printlnComments(string_list_ptr comments)
{
  if (comments != nullptr)
  {
    auto it_comments = comments->begin();
    for (int i = 0; i < comments->size(); ++i)
    {
      std::advance(it_comments, i);
      string_ptr comment = *it_comments;
      printComment(comment);
      println();
    }
  }
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterViewRenameStatement_ptr x)
{
  print0(ucase ? "ALTER VIEW " : "alter view ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  SQLName_ptr to = x->getTo();
  if (to != nullptr)
  {
    print0(ucase ? " RENAME TO " : " rename to ");
    printExpr(to);
  }

  SQLName_ptr changeOwnerTo = x->getChangeOwnerTo();
  if (changeOwnerTo != nullptr)
  {
    print0(ucase ? " CHANGEOWNER TO " : " changeowner to ");
    printExpr(changeOwnerTo);
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableAddPartition_ptr x)
{
  BOOL_ptr printAdd = BOOL::TRUE;
  if (instanceof <SQLObject, SQLAlterTableStatement>(x->getParent()))
  {
    SQLAlterTableStatement_ptr stmt = std::dynamic_pointer_cast<SQLAlterTableStatement>(x->getParent());
    // int p = stmt->getChildren().indexOf(x);
    int p = -1;
    auto it_getChildren = stmt->getChildren()->begin();
    for (int i = 0; i < stmt->getChildren()->size(); i++)
    {
      std::advance(it_getChildren, i);
      if ((*it_getChildren).get() == x.get())
      {
        p = i;
        break;
      }
    }

    std::advance(it_getChildren, p - 1);
    if (p > 0 && instanceof <SQLObject, SQLAlterTableAddPartition>(*it_getChildren))
    {
      printAdd = BOOL::FALSE;
    }
  }

  if (printAdd)
  {
    print0(ucase ? "ADD " : "add ");

    if (x->isIfNotExists())
    {
      print0(ucase ? "IF NOT EXISTS " : "if not exists ");
    }
  }
  else
  {
    print('\t');
    indentCount++;
  }

  if (x->getPartitionCount() != nullptr)
  {
    print0(ucase ? "PARTITION PARTITIONS " : "partition partitions ");
    x->getPartitionCount()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getPartitions()->size() > 0)
  {
    print0(ucase ? "PARTITION (" : "partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLObject, SQLObject>(x->getPartitions());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  SQLExpr_ptr location = x->getLocation();
  if (location != nullptr)
  {
    print0(ucase ? " LOCATION " : " location ");
    location->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (!printAdd)
  {
    indentCount--;
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableAddExtPartition_ptr x)
{
  print0(ucase ? "ADD " : "add ");

  MySqlExtPartition_ptr extPartition = x->getExtPartition();
  if (extPartition != nullptr)
  {
    println();
    extPartition->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDropExtPartition_ptr x)
{
  print0(ucase ? "DROP " : "drop ");

  MySqlExtPartition_ptr extPartition = x->getExtPartition();
  if (extPartition != nullptr)
  {
    println();
    extPartition->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableReOrganizePartition_ptr x)
{
  print0(ucase ? "REORGANIZE " : "reorganize ");

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getNames());
  printAndAccept(tmp1, make_string_ptr(", "));

  print0(ucase ? " INTO (" : " into (");
  SQLObject_list_ptr tmp2 = ListTran::tran_class<SQLObject, SQLObject>(x->getPartitions());
  printAndAccept(tmp2, make_string_ptr(", "));
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDropPartition_ptr x)
{
  BOOL_ptr printDrop = BOOL::TRUE;
  if (instanceof <SQLObject, SQLAlterTableStatement>(x->getParent()))
  {
    SQLAlterTableStatement_ptr stmt = std::dynamic_pointer_cast<SQLAlterTableStatement>(x->getParent());
    // int p = stmt->getChildren().indexOf(x);
    int p = -1;
    auto it_getChildren = stmt->getChildren()->begin();
    for (int i = 0; i < stmt->getChildren()->size(); i++)
    {
      std::advance(it_getChildren, i);
      if ((*it_getChildren).get() == x.get())
      {
        p = i;
        break;
      }
    }

    std::advance(it_getChildren, p - 1);
    if (p > 0 && instanceof <SQLObject, SQLAlterTableDropPartition>(*it_getChildren))
    {
      printDrop = BOOL::FALSE;
    }
  }

  if ((*printDrop) == BOOL::TRUE)
  {
    print0(ucase ? "DROP " : "drop ");

    if (x->isIfExists())
    {
      print0(ucase ? "IF EXISTS " : "if exists ");
    }
  }
  else
  {
    print('\t');
    indentCount++;
  }

  if (x->getAttribute("SIMPLE") != nullptr)
  {
    print0(ucase ? "PARTITION " : "partition ");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getPartitions());
    printAndAccept(tmp1, make_string_ptr(","));
  }
  else
  {
    print0(ucase ? "PARTITION (" : "partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getPartitions());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  if (x->isPurge())
  {
    print0(ucase ? " PURGE" : " purge");
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableRenamePartition_ptr x)
{
  print0(ucase ? "PARTITION (" : "partition (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartition());
  printAndAccept(tmp1, make_string_ptr(", "));
  print0(ucase ? ") RENAME TO PARTITION(" : ") rename to partition(");
  SQLObject_list_ptr tmp2 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getTo());
  printAndAccept(tmp2, make_string_ptr(", "));
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableSetComment_ptr x)
{
  print0(ucase ? "SET COMMENT " : "set comment ");
  x->getComment()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPrivilegeItem_ptr x)
{
  printExpr(x->getAction());

  if (!x->getColumns()->empty())
  {
    print0("(");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getColumns());
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(")");
  }
  return false;
}

// @Override
void SQLASTOutputVisitor::endVisit(SQLPrivilegeItem_ptr x)
{
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableSetLifecycle_ptr x)
{
  print0(ucase ? "SET LIFECYCLE " : "set lifecycle ");
  x->getLifecycle()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableSetLocation_ptr x)
{
  print0(ucase ? "SET LOCATION " : "set location ");
  x->getLocation()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableEnableLifecycle_ptr x)
{
  if (!x->getPartition()->empty())
  {
    print0(ucase ? "PARTITION (" : "partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartition());
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(") ");
  }

  print0(ucase ? "ENABLE LIFECYCLE" : "enable lifecycle");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableArchive_ptr x)
{
  if (!x->getPartition()->empty())
  {
    print0(ucase ? "PARTITION (" : "partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartition());
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(") ");
  }

  print0(ucase ? "ARCHIVE" : "archive");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDisableLifecycle_ptr x)
{
  if (!x->getPartition()->empty())
  {
    print0(ucase ? "PARTITION (" : "partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartition());
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(") ");
  }

  print0(ucase ? "DISABLE LIFECYCLE" : "disable lifecycle");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTablePartition_ptr x)
{
  if (!x->getPartition()->empty())
  {
    print0(ucase ? "PARTITION (" : "partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartition());
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(") ");
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTablePartitionSetProperties_ptr x)
{
  if (!x->getPartition()->empty())
  {
    print0(ucase ? "PARTITION (" : "partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartition());
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(") ");
  }

  if (!x->getPartitionProperties()->empty())
  {
    print0(ucase ? "SET PARTITIONPROPERTIES (" : "set partitionproperties (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartitionProperties());
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(") ");
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableTouch_ptr x)
{
  print0(ucase ? "TOUCH" : "touch");
  if (!x->getPartition()->empty())
  {
    print0(ucase ? " PARTITION (" : " partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartition());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLArrayExpr_ptr x)
{
  SQLExpr_ptr expr = x->getExpr();

  if (instanceof <SQLExpr, SQLIdentifierExpr>(expr) && 
      (std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->nameHashCode64() == FnvHash::Constants::ARRAY && 
      printNameQuote)
  {
    print0((std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->getName());
  }
  else if (expr != nullptr)
  {
    expr->accept(SharedObject(SQLASTOutputVisitor));
  }

  print('[');
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getValues());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(']');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLOpenStatement_ptr x)
{
  print0(ucase ? "OPEN " : "open ");
  printExpr(x->getCursorName(), parameterized);

  SQLName_list_ptr columns = x->getColumns();
  if (columns->size() > 0)
  {
    print('(');
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(columns);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  SQLExpr_ptr forExpr = x->getFor();
  if (forExpr != nullptr)
  {
    print0(ucase ? " FOR " : " for ");
    forExpr->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_list_ptr using_ = x->getUsing();
  if (using_->size() > 0)
  {
    print0(ucase ? " USING " : " using ");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(using_);
    printAndAccept(tmp1, make_string_ptr(", "));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLFetchStatement_ptr x)
{
  print0(ucase ? "FETCH " : "fetch ");
  x->getCursorName()->accept(SharedObject(SQLASTOutputVisitor));
  if (x->isBulkCollect())
  {
    print0(ucase ? " BULK COLLECT INTO " : " bulk collect into ");
  }
  else
  {
    print0(ucase ? " INTO " : " into ");
  }
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getInto());
  printAndAccept(tmp1, make_string_ptr(", "));

  SQLLimit_ptr limit = x->getLimit();
  if (limit != nullptr)
  {
    print(' ');
    limit->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCloseStatement_ptr x)
{
  print0(ucase ? "CLOSE " : "close ");
  printExpr(x->getCursorName(), parameterized);
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLGroupingSetExpr_ptr x)
{
  print0(ucase ? "GROUPING SETS" : "grouping sets");
  print0(" (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getParameters());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLIfStatement_ptr x)
{
  print0(ucase ? "IF " : "if ");
  x->getCondition()->accept(SharedObject(SQLASTOutputVisitor));
  this->indentCount++;
  println();
  auto it_getStatements = x->getStatements()->begin();
  for (int i = 0, size = x->getStatements()->size(); i < size; ++i)
  {
    std::advance(it_getStatements,i);
    SQLStatement_ptr item = *it_getStatements;
    item->accept(SharedObject(SQLASTOutputVisitor));
    if (i != size - 1)
    {
      println();
    }
  }
  this->indentCount--;

  for (SQLIfStatement_ElseIf_ptr elseIf : *x->getElseIfList())
  {
    println();
    elseIf->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getElseItem() != nullptr)
  {
    println();
    x->getElseItem()->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLIfStatement_Else_ptr x)
{
  print0(ucase ? "ELSE" : "else");
  this->indentCount++;
  println();

  auto it_getStatements = x->getStatements()->begin();
  for (int i = 0, size = x->getStatements()->size(); i < size; ++i)
  {
    if (i != 0)
    {
      println();
    }
    std::advance(it_getStatements, i);
    SQLStatement_ptr item = *it_getStatements;
    item->accept(SharedObject(SQLASTOutputVisitor));
  }

  this->indentCount--;
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLIfStatement_ElseIf_ptr x)
{
  print0(ucase ? "ELSE IF" : "else if");
  x->getCondition()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " THEN" : " then");
  this->indentCount++;
  println();

  auto it_getStatements = x->getStatements()->begin();
  for (int i = 0, size = x->getStatements()->size(); i < size; ++i)
  {
    if (i != 0)
    {
      println();
    }

    std::advance(it_getStatements, i);
    SQLStatement_ptr item = *it_getStatements;
    item->accept(SharedObject(SQLASTOutputVisitor));
  }

  this->indentCount--;
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLLoopStatement_ptr x)
{
  print0(ucase ? "LOOP" : "loop");
  this->indentCount++;
  println();

  auto it_getStatements = x->getStatements()->begin();
  for (int i = 0, size = x->getStatements()->size(); i < size; ++i)
  {
    std::advance(it_getStatements, i);
    SQLStatement_ptr item = *it_getStatements;
    item->accept(SharedObject(SQLASTOutputVisitor));

    if (i != size - 1)
    {
      println();
    }
  }

  this->indentCount--;
  println();
  print0(ucase ? "END LOOP" : "end loop");
  if (x->getLabelName() != nullptr)
  {
    print(' ');
    print0(x->getLabelName());
  }
  return false;
}

BOOL_ptr SQLASTOutputVisitor::visit(OracleFunctionDataType_ptr x)
{
  if (x->isStatic())
  {
    print0(ucase ? "STATIC " : "static ");
  }

  print0(ucase ? "FUNCTION " : "function ");

  print0(x->getName());

  print(" (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLParameter, SQLObject>(x->getParameters());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(")");
  print0(ucase ? " RETURN " : " return ");
  x->getReturnDataType()->accept(SharedObject(SQLASTOutputVisitor));

  SQLStatement_ptr block = x->getBlock();
  if (block != nullptr)
  {
    println();
    print0(ucase ? "IS" : "is");
    println();
    block->accept(SharedObject(SQLASTOutputVisitor));
  }

  return BOOL::FALSE;
}

BOOL_ptr SQLASTOutputVisitor::visit(OracleProcedureDataType_ptr x)
{
  if (x->isStatic())
  {
    print0(ucase ? "STATIC " : "static ");
  }

  print0(ucase ? "PROCEDURE " : "procedure ");

  print0(x->getName());

  if (x->getParameters()->size() > 0)
  {
    print(" (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLParameter, SQLObject>(x->getParameters());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(")");
  }

  SQLStatement_ptr block = x->getBlock();
  if (block != nullptr)
  {
    println();
    print0(ucase ? "IS" : "is");
    println();
    block->accept(SharedObject(SQLASTOutputVisitor));
  }

  return BOOL::FALSE;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLParameter_ptr x)
{
  SQLName_ptr name = x->getName();
  if (StringUtils::equalsIgnoreCase(x->getDataType()->getName(), make_string_ptr("CURSOR")))
  {
    print0(ucase ? "CURSOR " : "cursor ");
    x->getName()->accept(SharedObject(SQLASTOutputVisitor));
    print0(ucase ? " IS" : " is");
    this->indentCount++;
    println();
    SQLSelect_ptr select = (std::dynamic_pointer_cast<SQLQueryExpr>(x->getDefaultValue()))->getSubQuery();
    select->accept(SharedObject(SQLASTOutputVisitor));
    this->indentCount--;
  }
  else
  {
    if (x->isMap())
    {
      print0(ucase ? "MAP MEMBER " : "map member ");
    }
    else if (x->isOrder())
    {
      print0(ucase ? "ORDER MEMBER " : "order member ");
    }
    else if (x->isMember())
    {
      print0(ucase ? "MEMBER " : "member ");
    }
    SQLDataType_ptr dataType = x->getDataType();

    if (DbType::oracle.name->c_str() == dbType->name->c_str() ||
        instanceof <SQLDataType, OracleFunctionDataType>(dataType) ||
        instanceof <SQLDataType, OracleProcedureDataType>(dataType))
    {
      if (instanceof <SQLDataType, OracleFunctionDataType>(dataType))
      {
        OracleFunctionDataType_ptr functionDataType = std::dynamic_pointer_cast<OracleFunctionDataType>(dataType);
        visit(functionDataType);
        return false;
      }

      if (instanceof <SQLDataType, OracleProcedureDataType>(dataType))
      {
        OracleProcedureDataType_ptr procedureDataType = std::dynamic_pointer_cast<OracleProcedureDataType>(dataType);
        visit(procedureDataType);
        return false;
      }

      string_ptr dataTypeName = dataType->getName();
      BOOL_ptr printType = ((StringUtils::startWith(dataTypeName, make_string_ptr("TABLE OF")) &&
                            x->getDefaultValue() == nullptr) ||
                           StringUtils::equalsIgnoreCase(dataTypeName, make_string_ptr("REF CURSOR")) ||
                           StringUtils::startWith(dataTypeName, make_string_ptr("VARRAY(")))?BOOL::TRUE:BOOL::FALSE;
      if (printType)
      {
        print0(ucase ? "TYPE " : "type ");
      }

      name->accept(SharedObject(SQLASTOutputVisitor));
      if (x->getParamType()->name->c_str() == SQLParameter_ParameterType::IN.name->c_str())
      {
        print0(ucase ? " IN " : " in ");
      }
      else if (x->getParamType()->name->c_str() == SQLParameter_ParameterType::OUT.name->c_str())
      {
        print0(ucase ? " OUT " : " out ");
      }
      else if (x->getParamType()->name->c_str() == SQLParameter_ParameterType::INOUT.name->c_str())
      {
        print0(ucase ? " IN OUT " : " in out ");
      }
      else
      {
        print(' ');
      }

      if (x->isNoCopy())
      {
        print0(ucase ? "NOCOPY " : "nocopy ");
      }

      if (x->isConstant())
      {
        print0(ucase ? "CONSTANT " : "constant ");
      }

      if (printType)
      {
        print0(ucase ? "IS " : "is ");
      }
    }
    else
    {
      if (x->getParamType()->name->c_str() == SQLParameter_ParameterType::IN.name->c_str())
      {
        BOOL_ptr skip = (DbType::mysql.name->c_str() == dbType->name->c_str() &&
            instanceof <SQLObject, SQLCreateFunctionStatement>(x->getParent()))?BOOL::TRUE:BOOL::FALSE;

        if (!skip)
        {
          print0(ucase ? "IN " : "in ");
        }
      }
      else if (x->getParamType()->name->c_str() == SQLParameter_ParameterType::OUT.name->c_str())
      {
        print0(ucase ? "OUT " : "out ");
      }
      else if (x->getParamType()->name->c_str() == SQLParameter_ParameterType::INOUT.name->c_str())
      {
        print0(ucase ? "INOUT " : "inout ");
      }
      x->getName()->accept(SharedObject(SQLASTOutputVisitor));
      print(' ');
    }

    dataType->accept(SharedObject(SQLASTOutputVisitor));

    printParamDefaultValue(x);
  }

  return false;
}

void SQLASTOutputVisitor::printParamDefaultValue(SQLParameter_ptr x)
{
  if (x->getDefaultValue() != nullptr)
  {
    print0(" := ");
    x->getDefaultValue()->accept(SharedObject(SQLASTOutputVisitor));
  }
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDeclareItem_ptr x)
{
  SQLDataType_ptr dataType = x->getDataType();

  if (instanceof <SQLDataType, SQLRecordDataType>(dataType))
  {
    print0(ucase ? "TYPE " : "type ");
  }

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getType() == SQLDeclareItem::Type::TABLE)
  {
    print0(ucase ? " TABLE" : " table");
    int size = x->getTableElementList()->size();

    if (size > 0)
    {
      print0(" (");
      this->indentCount++;
      println();
      auto it_getTableElementList = x->getTableElementList()->begin();
      for (int i = 0; i < size; ++i)
      {
        if (i != 0)
        {
          print(',');
          println();
        }
        std::advance(it_getTableElementList, i);
        (*it_getTableElementList)->accept(SharedObject(SQLASTOutputVisitor));
      }
      this->indentCount--;
      println();
      print(')');
    }
  }
  else if (x->getType() == SQLDeclareItem::Type::CURSOR)
  {
    print0(ucase ? " CURSOR" : " cursor");
  }
  else
  {
    if (dataType != nullptr)
    {
      if (instanceof <SQLDataType, SQLRecordDataType>(dataType))
      {
        print0(ucase ? " IS " : " is ");
      }
      else
      {
        print(' ');
      }
      dataType->accept(SharedObject(SQLASTOutputVisitor));
    }
    if (x->getValue() != nullptr)
    {
      if (DbType::mysql.name->c_str() == getDbType()->name->c_str())
      {
        print0(ucase ? " DEFAULT " : " default ");
      }
      else
      {
        print0(" = ");
      }
      x->getValue()->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPartitionValue_ptr x)
{
  if (x->getOperator() == SQLPartitionValue_Operator::LessThan //
      && DbType::oracle.name->c_str() != getDbType()->name->c_str() &&
       x->getItems()->size() == 1)
  { //
    auto it_getItems = x->getItems()->begin();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(*(it_getItems)))
    {
      SQLIdentifierExpr_ptr ident = std::dynamic_pointer_cast<SQLIdentifierExpr>(*(it_getItems));
      if (StringUtils::equalsIgnoreCase(make_string_ptr("MAXVALUE"), ident->getName()))
      {
        print0(ucase ? "VALUES LESS THAN MAXVALUE" : "values less than maxvalue");
        return false;
      }
    }
  }

  if (x->getOperator() == SQLPartitionValue_Operator::LessThan)
  {
    print0(ucase ? "VALUES LESS THAN (" : "values less than (");
  }
  else if (x->getOperator() == SQLPartitionValue_Operator::In)
  {
    print0(ucase ? "VALUES IN (" : "values in (");
  }
  else
  {
    print(ucase ? "VALUES (" : "values (");
  }
  // SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getItems());
  printAndAccept(x->getItems(), make_string_ptr(", "), BOOL::FALSE);
  print(')');
  return false;
}

DbType_ptr SQLASTOutputVisitor::getDbType()
{
  return dbType;
}

BOOL_ptr SQLASTOutputVisitor::isUppCase()
{
  return ucase?BOOL::TRUE:BOOL::FALSE;
}

void SQLASTOutputVisitor::setUppCase(BOOL_ptr val)
{
  this->config(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputUCase)), val);
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPartition_ptr x)
{
  BOOL_ptr isDbPartiton = BOOL::FALSE, isTbPartition = BOOL::FALSE;
  SQLObject_ptr parent = x->getParent();
  if (parent != nullptr)
  {
    SQLObject_ptr parent2 = parent->getParent();
    if (instanceof <SQLObject, MySqlCreateTableStatement>(parent2))
    {
      MySqlCreateTableStatement_ptr stmt = std::dynamic_pointer_cast<MySqlCreateTableStatement>(parent2);
      isDbPartiton = (parent.get() == stmt->getDbPartitionBy().get())?BOOL::TRUE:BOOL::FALSE;
      isTbPartition = (parent.get() == stmt->getTablePartitionBy().get())?BOOL::TRUE:BOOL::FALSE;
    }
  }

  if (isDbPartiton)
  {
    print0(ucase ? "DBPARTITION " : "dbpartition ");
  }
  else if (isTbPartition)
  {
    print0(ucase ? "TBPARTITION " : "tbpartition ");
  }
  else
  {
    print0(ucase ? "PARTITION " : "partition ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  if (x->getValues() != nullptr)
  {
    print(' ');
    x->getValues()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getDataDirectory() != nullptr)
  {
    this->indentCount++;
    println();
    print0(ucase ? "DATA DIRECTORY " : "data directory ");
    x->getDataDirectory()->accept(SharedObject(SQLASTOutputVisitor));
    this->indentCount--;
  }

  if (x->getIndexDirectory() != nullptr)
  {
    this->indentCount++;
    println();
    print0(ucase ? "INDEX DIRECTORY " : "index directory ");
    x->getIndexDirectory()->accept(SharedObject(SQLASTOutputVisitor));
    this->indentCount--;
  }

  this->indentCount++;
  printOracleSegmentAttributes(x);

  if (x->getEngine() != nullptr)
  {
    println();
    print0(ucase ? "STORAGE ENGINE " : "storage engine ");
    x->getEngine()->accept(SharedObject(SQLASTOutputVisitor));
  }
  this->indentCount--;

  if (x->getMaxRows() != nullptr)
  {
    print0(ucase ? " MAX_ROWS " : " max_rows ");
    x->getMaxRows()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getMinRows() != nullptr)
  {
    print0(ucase ? " MIN_ROWS " : " min_rows ");
    x->getMinRows()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getComment() != nullptr)
  {
    print0(ucase ? " COMMENT " : " comment ");
    x->getComment()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getSubPartitionsCount() != nullptr)
  {
    this->indentCount++;
    println();
    print0(ucase ? "SUBPARTITIONS " : "subpartitions ");
    x->getSubPartitionsCount()->accept(SharedObject(SQLASTOutputVisitor));
    this->indentCount--;
  }

  if (x->getSubPartitions()->size() > 0)
  {
    print(" (");
    this->indentCount++;
    auto it_getSubPartitions = x->getSubPartitions()->begin();
    for (int i = 0; i < x->getSubPartitions()->size(); ++i)
    {
      if (i != 0)
      {
        print(',');
      }
      println();
      std::advance(it_getSubPartitions,i);
      (*it_getSubPartitions)->accept(SharedObject(SQLASTOutputVisitor));
    }
    this->indentCount--;
    println();
    print(')');
  }

  SQLExpr_ptr locality = x->getLocality();
  if (locality != nullptr)
  {
    print(ucase ? " LOCALITY = " : " locality = ");
    locality->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPartitionByRange_ptr x)
{
  SQLExpr_ptr interval = x->getInterval();
  if (x->getColumns()->empty() && (instanceof <SQLExpr, SQLBetweenExpr>(interval) ||
                                   instanceof <SQLExpr, SQLMethodInvokeExpr>(interval)))
  {
    interval->accept(SharedObject(SQLASTOutputVisitor));
  }
  else
  {
    print0(ucase ? "RANGE" : "range");

    BOOL_ptr columns = BOOL::TRUE;
    for (SQLExpr_ptr column : *x->getColumns())
    {
      if (!(instanceof <SQLExpr, SQLName>(column)))
      {
        columns = BOOL::FALSE;
        break;
      }
    }

    if (x->getColumns()->size() == 1)
    {
      if (DbType::mysql.name->c_str() == getDbType()->name->c_str() && columns)
      {
        print0(ucase ? " COLUMNS (" : " columns (");
      }
      else
      {
        print0(" (");
      }
      auto it_getColumns = x->getColumns()->begin();
      (*it_getColumns)->accept(SharedObject(SQLASTOutputVisitor));
      print(')');
    }
    else
    {
      if (DbType::mysql.name->c_str() == getDbType()->name->c_str() && columns)
      {
        print0(ucase ? " COLUMNS (" : " columns (");
      }
      else
      {
        print0(" (");
      }
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getColumns());
      printAndAccept(tmp1, make_string_ptr(", "));
      print(')');
    }

    if (interval != nullptr)
    {
      print0(ucase ? " INTERVAL (" : " interval (");
      interval->accept(SharedObject(SQLASTOutputVisitor));
      print(')');
    }
  }

  printPartitionsCountAndSubPartitions(x);

  if (x->getPartitions()->size() > 0)
  {
    print(" (");
    this->indentCount++;
    auto it_getPartitions = x->getPartitions()->begin();
    for (int i = 0, size = x->getPartitions()->size(); i < size; ++i)
    {
      if (i != 0)
      {
        print(',');
      }
      println();
      std::advance(it_getPartitions, i);
      (*it_getPartitions)->accept(SharedObject(SQLASTOutputVisitor));
    }
    this->indentCount--;
    println();
    print(')');
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPartitionByList_ptr x)
{
  print0(ucase ? "LIST " : "list ");
  if (x->getColumns()->size() == 1)
  {
    print('(');
    (*(x->getColumns()->begin()))->accept(SharedObject(SQLASTOutputVisitor));
    print0(")");
  }
  else
  {
    print0(ucase ? "COLUMNS (" : "columns (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getColumns());
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(")");
  }

  printPartitionsCountAndSubPartitions(x);

  printSQLPartitions(x->getPartitions());
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPartitionByHash_ptr x)
{
  if (x->isLinear())
  {
    print0(ucase ? "LINEAR HASH " : "linear hash ");
  }
  else
  {
    if (x->isUnique())
    {
      print0(ucase ? "UNI_HASH " : "uni_hash ");
    }
    else
    {
      print0(ucase ? "HASH " : "hash ");
    }
  }

  if (x->isKey())
  {
    print0(ucase ? "KEY" : "key");
  }

  print('(');
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getColumns());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  printPartitionsCountAndSubPartitions(x);

  printSQLPartitions(x->getPartitions());

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPartitionByValue_ptr x)
{
  print0(ucase ? "VALUE " : "value ");

  print('(');
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getColumns());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  printPartitionsCountAndSubPartitions(x);

  printSQLPartitions(x->getPartitions());

  return false;
}

void SQLASTOutputVisitor::printSQLPartitions(SQLPartition_list_ptr partitions)
{
  int partitionsSize = partitions->size();
  if (partitionsSize > 0)
  {
    print0(" (");
    this->indentCount++;
    auto it_partitions = partitions->begin();
    for (int i = 0; i < partitionsSize; ++i)
    {
      println();
      std::advance(it_partitions,i);
      (*it_partitions)->accept(SharedObject(SQLASTOutputVisitor));
      if (i != partitionsSize - 1)
      {
        print0(", ");
      }
    }
    this->indentCount--;
    println();
    print(')');
  }
}

void SQLASTOutputVisitor::printPartitionsCountAndSubPartitions(SQLPartitionBy_ptr x)
{
  SQLExpr_ptr partitionsCount = x->getPartitionsCount();
  if (partitionsCount != nullptr)
  {
    BOOL_ptr isDbPartiton = BOOL::FALSE, isTbPartition = BOOL::FALSE;
    if (instanceof <SQLObject, MySqlCreateTableStatement>(x->getParent()))
    {
      MySqlCreateTableStatement_ptr stmt = std::dynamic_pointer_cast<MySqlCreateTableStatement>(x->getParent());
      isDbPartiton = (x == std::dynamic_pointer_cast<SQLPartitionBy>(stmt->getDbPartitionBy()))?BOOL::TRUE:BOOL::FALSE;
      isTbPartition = (x == std::dynamic_pointer_cast<SQLPartitionBy>(stmt->getTablePartitionBy()))?BOOL::TRUE:BOOL::FALSE;
    }

    // if (BOOL::TRUE.equals(x->getAttribute("ads.partition"))) {
    //     print0(ucase ? " PARTITION NUM " : " partition num ");
    // } else {
    //     if (isDbPartiton) {
    //         print0(ucase ? " DBPARTITIONS " : " dbpartitions ");
    //     } else if (isTbPartition) {
    //         print0(ucase ? " TBPARTITIONS " : " tbpartitions ");
    //     } else {
    //         print0(ucase ? " PARTITIONS " : " partitions ");
    //     }
    // }

    partitionsCount->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getLifecycle() != nullptr)
  {
    print0(ucase ? " LIFECYCLE " : " lifecycle ");
    x->getLifecycle()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getSubPartitionBy() != nullptr)
  {
    println();
    x->getSubPartitionBy()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getStoreIn()->size() > 0)
  {
    println();
    print0(ucase ? "STORE IN (" : "store in (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getStoreIn());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSubPartitionByHash_ptr x)
{
  if (x->isLinear())
  {
    print0(ucase ? "SUBPARTITION BY LINEAR HASH " : "subpartition by linear hash ");
  }
  else
  {
    print0(ucase ? "SUBPARTITION BY HASH " : "subpartition by hash ");
  }

  if (x->isKey())
  {
    print0(ucase ? "KEY" : "key");
  }

  print('(');
  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  print(')');

  if (x->getSubPartitionsCount() != nullptr)
  {
    print0(ucase ? " SUBPARTITIONS " : " subpartitions ");
    x->getSubPartitionsCount()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSubPartitionByRange_ptr x)
{
  print0(ucase ? "SUBPARTITION BY RANGE " : "subpartition by range ");

  SQLExpr_ptr subPartitionsCount = x->getSubPartitionsCount();
  if (subPartitionsCount != nullptr)
  {
    print0(ucase ? " SUBPARTITIONS " : " subpartitions ");
    subPartitionsCount->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSubPartitionByList_ptr x)
{
  if (x->isLinear())
  {
    print0(ucase ? "SUBPARTITION BY LINEAR HASH " : "subpartition by linear hash ");
  }
  else
  {
    print0(ucase ? "SUBPARTITION BY HASH " : "subpartition by hash ");
  }

  print('(');
  x->getColumn()->accept(SharedObject(SQLASTOutputVisitor));
  print(')');

  if (x->getSubPartitionsCount() != nullptr)
  {
    print0(ucase ? " SUBPARTITIONS " : " subpartitions ");
    x->getSubPartitionsCount()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getSubPartitionTemplate()->size() > 0)
  {
    this->indentCount++;
    println();
    print0(ucase ? "SUBPARTITION TEMPLATE (" : "subpartition template (");
    this->indentCount++;
    println();
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSubPartition, SQLObject>(x->getSubPartitionTemplate());
    printlnAndAccept(tmp1, make_string_ptr(","));
    this->indentCount--;
    println();
    print(')');
    this->indentCount--;
  }

  if (x->getLifecycle() != nullptr)
  {
    print0(ucase ? " LIFECYCLE " : " lifecycle ");
    x->getLifecycle()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSubPartition_ptr x)
{
  print0(ucase ? "SUBPARTITION " : "subpartition ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getValues() != nullptr)
  {
    print(' ');
    x->getValues()->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLName_ptr tableSpace = x->getTableSpace();
  if (tableSpace != nullptr)
  {
    print0(ucase ? " TABLESPACE " : " tablespace ");
    tableSpace->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterDatabaseStatement_ptr x)
{
  print0(ucase ? "ALTER DATABASE " : "alter database ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  if (x->isUpgradeDataDirectoryName())
  {
    print0(ucase ? " UPGRADE DATA DIRECTORY NAME" : " upgrade data directory name");
  }

  if (x->getProperties()->size() > 0)
  {
    print0(ucase ? " SET DBPROPERTIES (" : "set dbproperties (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getProperties());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  SQLAlterDatabaseItem_ptr item = x->getItem();
  if (instanceof <SQLAlterDatabaseItem, MySqlAlterDatabaseSetOption>(item))
  {
    MySqlAlterDatabaseSetOption_ptr setOption = std::dynamic_pointer_cast<MySqlAlterDatabaseSetOption>(item);
    print0(ucase ? " SET " : " set ");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(setOption->getOptions());
    printAndAccept(tmp1, make_string_ptr(", "));

    SQLName_ptr on = setOption->getOn();
    if (on != nullptr)
    {
      print0(ucase ? " ON " : " on ");
      on->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  if (instanceof <SQLAlterDatabaseItem, MySqlAlterDatabaseKillJob>(item))
  {
    print0(ucase ? " KILL " : " kill ");
    MySqlAlterDatabaseKillJob_ptr kill = std::dynamic_pointer_cast<MySqlAlterDatabaseKillJob>(item);
    kill->getJobType()->accept(SharedObject(SQLASTOutputVisitor));
    print0(" ");
    kill->getJobId()->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLAlterCharacter_ptr character = x->getCharacter();
  if (character != nullptr)
  {
    print(' ');
    character->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableConvertCharSet_ptr x)
{
  print0(ucase ? "CONVERT TO CHARACTER SET " : "convert to character set ");
  x->getCharset()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getCollate() != nullptr)
  {
    print0(ucase ? " COLLATE " : " collate ");
    x->getCollate()->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableCoalescePartition_ptr x)
{
  print0(ucase ? "COALESCE PARTITION " : "coalesce partition ");
  x->getCount()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableTruncatePartition_ptr x)
{
  print0(ucase ? "TRUNCATE PARTITION " : "truncate partition ");
  printPartitions(x->getPartitions());
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableDiscardPartition_ptr x)
{
  print0(ucase ? "DISCARD PARTITION " : "discard partition ");
  printPartitions(x->getPartitions());

  if (x->isTablespace())
  {
    print0(ucase ? " TABLESPACE" : " tablespace");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableExchangePartition_ptr x)
{
  print0(ucase ? "EXCHANGE PARTITION " : "exchange partition ");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getPartitions());
  printAndAccept(tmp1, make_string_ptr(", "));
  print0(ucase ? " WITH TABLE " : " with table ");
  x->getTable()->accept(SharedObject(SQLASTOutputVisitor));

  BOOL_ptr validation = x->getValidation();
  if (validation != nullptr)
  {
    if (validation)
    {
      print0(ucase ? " WITH VALIDATION" : " with validation");
    }
    else
    {
      print0(ucase ? " WITHOUT VALIDATION" : " without validation");
    }
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableImportPartition_ptr x)
{
  print0(ucase ? "IMPORT PARTITION " : "import partition ");
  printPartitions(x->getPartitions());

  if (x->isTablespace())
  {
    print0(ucase ? " TABLESPACE" : " tablespace");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableAnalyzePartition_ptr x)
{
  print0(ucase ? "ANALYZE PARTITION " : "analyze partition ");

  printPartitions(x->getPartitions());
  return false;
}

void SQLASTOutputVisitor::printPartitions(SQLName_list_ptr partitions)
{
  if (partitions->size() == 1)
  {
    auto it_partitions = partitions->begin();
    if (StringUtils::equalsIgnoreCase(make_string_ptr("ALL"), ((*it_partitions)->getSimpleName())))
    {
      print0(ucase ? "ALL" : "all");
    }
  }
  else
  {
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(partitions);
    printAndAccept(tmp1, make_string_ptr(", "));
  }
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableCheckPartition_ptr x)
{
  print0(ucase ? "CHECK PARTITION " : "check partition ");
  printPartitions(x->getPartitions());
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableOptimizePartition_ptr x)
{
  print0(ucase ? "OPTIMIZE PARTITION " : "optimize partition ");
  printPartitions(x->getPartitions());
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableRebuildPartition_ptr x)
{
  print0(ucase ? "REBUILD PARTITION " : "rebuild partition ");
  printPartitions(x->getPartitions());
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableRepairPartition_ptr x)
{
  print0(ucase ? "REPAIR PARTITION " : "repair partition ");
  printPartitions(x->getPartitions());
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSequenceExpr_ptr x)
{
  x->getSequence()->accept(SharedObject(SQLASTOutputVisitor));
  print('.');
  print0(ucase ? x->getFunction()->name : x->getFunction()->nameLCase);
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLMergeStatement_ptr x)
{
  print0(ucase ? "MERGE " : "merge ");
  if (x->getHints()->size() > 0)
  {
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLHint, SQLObject>(x->getHints());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(' ');
  }

  print0(ucase ? "INTO " : "into ");
  x->getInto()->accept(SharedObject(SQLASTOutputVisitor));

  println();
  print0(ucase ? "USING " : "using_ ");
  x->getUsing()->accept(SharedObject(SQLASTOutputVisitor));

  print0(ucase ? " ON (" : " on (");
  x->getOn()->accept(SharedObject(SQLASTOutputVisitor));
  print0(") ");

  if (x->getUpdateClause() != nullptr)
  {
    println();
    x->getUpdateClause()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getInsertClause() != nullptr)
  {
    println();
    x->getInsertClause()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getErrorLoggingClause() != nullptr)
  {
    println();
    x->getErrorLoggingClause()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLMergeStatement_MergeUpdateClause_ptr x)
{
  if (x->isDelete())
  {
    print0(ucase ? "WHEN MATCHED THEN DELETE" : "when matched then delete");
    return false;
  }
  print0(ucase ? "WHEN MATCHED THEN UPDATE SET " : "when matched then update set ");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLUpdateSetItem, SQLObject>(x->getItems());
  printAndAccept(tmp1, make_string_ptr(", "));

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    this->indentCount++;
    println();
    print0(ucase ? "WHERE " : "where ");
    printExpr(where, parameterized);
    this->indentCount--;
  }

  SQLExpr_ptr deleteWhere = x->getDeleteWhere();
  if (deleteWhere != nullptr)
  {
    this->indentCount++;
    println();
    print0(ucase ? "DELETE WHERE " : "delete where ");
    printExpr(deleteWhere, parameterized);
    this->indentCount--;
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLMergeStatement_MergeInsertClause_ptr x)
{
  print0(ucase ? "WHEN NOT MATCHED THEN INSERT" : "when not matched then insert");
  if (x->getColumns()->size() > 0)
  {
    print(" (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getColumns());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }
  print0(ucase ? " VALUES (" : " values (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getValues());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');
  if (x->getWhere() != nullptr)
  {
    this->indentCount++;
    println();
    print0(ucase ? "WHERE " : "where ");
    x->getWhere()->accept(SharedObject(SQLASTOutputVisitor));
    this->indentCount--;
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLErrorLoggingClause_ptr x)
{
  print0(ucase ? "LOG ERRORS " : "log errors ");
  if (x->getInto() != nullptr)
  {
    print0(ucase ? "INTO " : "into ");
    x->getInto()->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }

  if (x->getSimpleExpression() != nullptr)
  {
    print('(');
    x->getSimpleExpression()->accept(SharedObject(SQLASTOutputVisitor));
    print(')');
  }

  if (x->getLimit() != nullptr)
  {
    print0(ucase ? " REJECT LIMIT " : " reject limit ");
    x->getLimit()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCreateSequenceStatement_ptr x)
{
  print0(ucase ? "CREATE " : "create ");

  if (x->isGroup())
  {
    print0(ucase ? "GROUP " : "group ");
  }
  else if (x->isSimple())
  {
    print0(ucase ? "SIMPLE " : "simple ");
    BOOL_ptr cache = x->getWithCache();
    if (cache)
    {
      print0(ucase ? "WITH CACHE " : "with cache ");
    }
  }
  else if (x->isTime())
  {
    print0(ucase ? "TIME " : "time ");
  }

  print0(ucase ? "SEQUENCE " : "sequence ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getStartWith() != nullptr)
  {
    print0(ucase ? " START WITH " : " start with ");
    x->getStartWith()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getIncrementBy() != nullptr)
  {
    print0(ucase ? " INCREMENT BY " : " increment by ");
    x->getIncrementBy()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getMaxValue() != nullptr)
  {
    print0(ucase ? " MAXVALUE " : " maxvalue ");
    x->getMaxValue()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isNoMaxValue())
  {
    if (DbType::postgresql.name->c_str() == dbType->name->c_str())
    {
      print0(ucase ? " NO MAXVALUE" : " no maxvalue");
    }
    else
    {
      print0(ucase ? " NOMAXVALUE" : " nomaxvalue");
    }
  }

  if (x->getMinValue() != nullptr)
  {
    print0(ucase ? " MINVALUE " : " minvalue ");
    x->getMinValue()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isNoMinValue())
  {
    if (DbType::postgresql.name->c_str() == dbType->name->c_str())
    {
      print0(ucase ? " NO MINVALUE" : " no minvalue");
    }
    else
    {
      print0(ucase ? " NOMINVALUE" : " nominvalue");
    }
  }

  if (x->getCycle() != nullptr)
  {
    if (x->getCycle())
    {
      print0(ucase ? " CYCLE" : " cycle");
    }
    else
    {
      if (DbType::postgresql.name->c_str() == dbType->name->c_str())
      {
        print0(ucase ? " NO CYCLE" : " no cycle");
      }
      else
      {
        print0(ucase ? " NOCYCLE" : " nocycle");
      }
    }
  }

  BOOL_ptr cache = x->getCache();
  // if (cache != nullptr) {
  if (cache)
  {
    print0(ucase ? " CACHE" : " cache");

    SQLExpr_ptr cacheValue = x->getCacheValue();
    if (cacheValue != nullptr)
    {
      print(' ');
      cacheValue->accept(SharedObject(SQLASTOutputVisitor));
    }
  }
  else
  {
    print0(ucase ? " NOCACHE" : " nocache");
  }
  // }

  BOOL_ptr order = x->getOrder();
  // if (order != nullptr) {
  if (order)
  {
    print0(ucase ? " ORDER" : " order");
  }
  else
  {
    print0(ucase ? " NOORDER" : " noorder");
  }
  // }

  SQLExpr_ptr unitCount = x->getUnitCount();
  if (unitCount != nullptr)
  {
    print0(ucase ? " UNIT COUNT " : " unit count ");
    printExpr(unitCount);
  }

  SQLExpr_ptr unitIndex = x->getUnitIndex();
  if (unitIndex != nullptr)
  {
    print0(ucase ? " INDEX " : " index ");
    printExpr(unitIndex);
  }

  if (x->getStep() != nullptr)
  {
    print0(ucase ? " STEP " : " step ");
    printExpr(x->getStep());
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterSequenceStatement_ptr x)
{
  print0(ucase ? "ALTER SEQUENCE " : "alter sequence ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isChangeToSimple())
  {
    print0(ucase ? " CHANGE TO SIMPLE" : " change to simple");
    BOOL_ptr cache = x->getWithCache();
    if (cache)
    {
      print0(ucase ? " WITH CACHE" : "  with cache");
    }
  }
  else if (x->isChangeToGroup())
  {
    print0(ucase ? " CHANGE TO GROUP" : " change to group");
  }
  else if (x->isChangeToTime())
  {
    print0(ucase ? " CHANGE TO TIME" : " change to time");
  }

  if (x->getStartWith() != nullptr)
  {
    print0(ucase ? " START WITH " : " start with ");
    x->getStartWith()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getIncrementBy() != nullptr)
  {
    print0(ucase ? " INCREMENT BY " : " increment by ");
    x->getIncrementBy()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getMaxValue() != nullptr)
  {
    print0(ucase ? " MAXVALUE " : " maxvalue ");
    x->getMaxValue()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isNoMaxValue())
  {
    if (DbType::postgresql.name->c_str() == dbType->name->c_str())
    {
      print0(ucase ? " NO MAXVALUE" : " no maxvalue");
    }
    else
    {
      print0(ucase ? " NOMAXVALUE" : " nomaxvalue");
    }
  }

  if (x->getMinValue() != nullptr)
  {
    print0(ucase ? " MINVALUE " : " minvalue ");
    x->getMinValue()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isNoMinValue())
  {
    if (DbType::postgresql.name->c_str() == dbType->name->c_str())
    {
      print0(ucase ? " NO MINVALUE" : " no minvalue");
    }
    else
    {
      print0(ucase ? " NOMINVALUE" : " nominvalue");
    }
  }

  if (x->getCycle() != nullptr)
  {
    if (x->getCycle())
    {
      print0(ucase ? " CYCLE" : " cycle");
    }
    else
    {
      if (DbType::postgresql.name->c_str() == dbType->name->c_str())
      {
        print0(ucase ? " NO CYCLE" : " no cycle");
      }
      else
      {
        print0(ucase ? " NOCYCLE" : " nocycle");
      }
    }
  }

  BOOL_ptr cache = x->getCache();
  // if (cache != nullptr) {
  if (cache)
  {
    print0(ucase ? " CACHE" : " cache");

    SQLExpr_ptr cacheValue = x->getCacheValue();
    if (cacheValue != nullptr)
    {
      print(' ');
      cacheValue->accept(SharedObject(SQLASTOutputVisitor));
    }
  }
  else
  {
    print0(ucase ? " NOCACHE" : " nocache");
  }
  // }

  BOOL_ptr order = x->getOrder();
  // if (order != nullptr) {
  if (order)
  {
    print0(ucase ? " ORDER" : " order");
  }
  else
  {
    print0(ucase ? " NOORDER" : " noorder");
  }
  // }

  if (x->isRestart())
  {
    print0(ucase ? " RESTART" : " restart");

    SQLExpr_ptr restartWith = x->getRestartWith();
    if (restartWith != nullptr)
    {
      print0(ucase ? " WITH " : " with ");
      restartWith->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLDateExpr_ptr x)
{
  string_ptr literal = x->getLiteral();
  print0(ucase ? "DATE '" : "date '");
  print0(literal);
  print('\'');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLTimeExpr_ptr x)
{
  SQLExpr_ptr literal = x->getLiteral();
  print0(ucase ? "TIME " : "time ");
  printExpr(literal, parameterized);

  return false;
}

bool SQLASTOutputVisitor::visit(SQLDateTimeExpr_ptr x)
{
  SQLExpr_ptr literal = x->getLiteral();
  print0(ucase ? "DATETIME " : "datetime ");
  printExpr(literal, parameterized);

  return false;
}

bool SQLASTOutputVisitor::visit(SQLRealExpr_ptr x)
{
  double value = x->getValue();
  print0(ucase ? "REAL '" : "real '");
  print(value);
  print('\'');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLDecimalExpr_ptr x)
{
  double value = x->getValue();
  print0(ucase ? "DECIMAL '" : "decimal '");
  print(std::to_string(value));
  print('\'');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLDoubleExpr_ptr x)
{
  double value = x->getValue();
  print0(ucase ? "DOUBLE '" : "double '");
  print(value);
  print('\'');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLFloatExpr_ptr x)
{
  double value = x->getValue();
  print0(ucase ? "FLOAT '" : "float '");
  print(value);
  print('\'');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLSmallIntExpr_ptr x)
{
  double value = x->getValue();
  print0(ucase ? "SMALLINT '" : "smallint '");
  print(value);
  print('\'');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLTinyIntExpr_ptr x)
{
  uint16_t value = x->getValue();
  print0(ucase ? "TINYINT '" : "tinyint '");
  print(value);
  print('\'');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLBigIntExpr_ptr x)
{
  long value = x->getValue();
  print0(ucase ? "BIGINT '" : "bigint '");
  print(value);
  print('\'');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLLimit_ptr x)
{
  print0(ucase ? "LIMIT " : "limit ");
  SQLExpr_ptr offset = x->getOffset();
  if (offset != nullptr)
  {
    printExpr(offset, parameterized);
    print0(", ");
  }

  SQLExpr_ptr rowCount = x->getRowCount();
  if (rowCount != nullptr)
  {
    printExpr(rowCount, parameterized);
  }

  SQLExpr_list_ptr by = x->getBy();
  if (by != nullptr)
  {
    print0(ucase ? " BY " : " by ");
    auto it_by = by->begin();
    for (int i = 0; i < by->size(); i++)
    {
      if (i != 0)
      {
        print0(", ");
      }

      std::advance(it_by, i);
      (*it_by)->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLDescribeStatement_ptr x)
{
  print0(ucase ? "DESC " : "desc ");
  if (x->getObjectType() != nullptr)
  {
    print0(x->getObjectType()->name);
    print(' ');
  }

  if (x->isExtended())
  {
    print0(ucase ? "EXTENDED " : "extended ");
  }

  if (x->isFormatted())
  {
    print0(ucase ? "FORMATTED " : "formatted ");
  }

  if (x->getObject() != nullptr)
  {
    x->getObject()->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLName_ptr column = x->getColumn();
  if (column != nullptr)
  {
    print(' ');
    column->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getPartition()->size() > 0)
  {
    print0(ucase ? " PARTITION (" : " partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getPartition());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }
  return false;
}

void SQLASTOutputVisitor::printHierarchical(SQLSelectQueryBlock_ptr x)
{
  SQLExpr_ptr startWith = x->getStartWith(), connectBy = x->getConnectBy();
  if (startWith != nullptr || connectBy != nullptr)
  {
    println();
    if (x->getStartWith() != nullptr)
    {
      print0(ucase ? "START WITH " : "start with ");
      x->getStartWith()->accept(SharedObject(SQLASTOutputVisitor));
      println();
    }

    print0(ucase ? "CONNECT BY " : "connect by ");

    if (x->isNoCycle())
    {
      print0(ucase ? "NOCYCLE " : "nocycle ");
    }

    if (x->isPrior())
    {
      print0(ucase ? "PRIOR " : "prior ");
    }

    x->getConnectBy()->accept(SharedObject(SQLASTOutputVisitor));
  }
}

void SQLASTOutputVisitor::printOracleSegmentAttributes(OracleSegmentAttributes_ptr x)
{
  // if (x->getPctfree() != nullptr)
  {
    println();
    print0(ucase ? "PCTFREE " : "pctfree ");
    print(x->getPctfree());
  }

  // if (x->getPctused() != nullptr)
  {
    println();
    print0(ucase ? "PCTUSED " : "pctused ");
    print(x->getPctused());
  }

  // if (x->getInitrans() != nullptr)
  {
    println();
    print0(ucase ? "INITRANS " : "initrans ");
    print(x->getInitrans());
  }

  // if (x->getMaxtrans() != nullptr)
  {
    println();
    print0(ucase ? "MAXTRANS " : "maxtrans ");
    print(x->getMaxtrans());
  }

  if ((*x->getCompress()) == BOOL::FALSE)
  {
    println();
    print0(ucase ? "NOCOMPRESS" : "nocompress");
  }
  else if ((*x->getCompress()) == BOOL::TRUE)
  {
    println();
    print0(ucase ? "COMPRESS" : "compress");

    // if (x->getCompressLevel() != nullptr)
    {
      print(' ');
      print(x->getCompressLevel());
    }
  }

  if (x->getLogging() == BOOL::TRUE)
  {
    println();
    print0(ucase ? "LOGGING" : "logging");
  }
  else if (x->getLogging() == BOOL::FALSE)
  {
    println();
    print0(ucase ? "NOLOGGING" : "nologging");
  }

  if (x->getTablespace() != nullptr)
  {
    println();
    print0(ucase ? "TABLESPACE " : "tablespace ");
    x->getTablespace()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getStorage() != nullptr)
  {
    println();
    x->getStorage()->accept(SharedObject(SQLASTOutputVisitor));
  }
}

// @Override
bool SQLASTOutputVisitor::visit(SQLWhileStatement_ptr x)
{
  string_ptr label = x->getLabelName();

  if (label != nullptr && label->length() != 0)
  {
    print0(x->getLabelName());
    print0(": ");
  }
  print0(ucase ? "WHILE " : "while ");
  x->getCondition()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " DO" : " do");
  println();
  auto it_getStatements = x->getStatements()->begin();
  for (int i = 0, size = x->getStatements()->size(); i < size; ++i)
  {
    std::advance(it_getStatements, i);
    SQLStatement_ptr item = *it_getStatements;
    item->accept(SharedObject(SQLASTOutputVisitor));
    if (i != size - 1)
    {
      println();
    }
  }
  println();
  print0(ucase ? "END WHILE" : "end while");
  if (label != nullptr && label->length() != 0)
  {
    print(' ');
    print0(label);
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDeclareStatement_ptr x)
{
  BOOL_ptr printDeclare = (!(instanceof <SQLObject, OracleCreatePackageStatement>(x->getParent())))?BOOL::TRUE:BOOL::FALSE;
  if (printDeclare)
  {
    print0(ucase ? "DECLARE " : "declare ");
  }
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLDeclareItem, SQLObject>(x->getItems());
  this->printAndAccept(tmp1, make_string_ptr(", "));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLReturnStatement_ptr x)
{
  print0(ucase ? "RETURN" : "return");

  if (x->getExpr() != nullptr)
  {
    print(' ');
    x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

void SQLASTOutputVisitor::postVisit(SQLObject_ptr x)
{
  if (instanceof <SQLObject, SQLStatement>(x))
  {
    SQLStatement_ptr stmt = std::dynamic_pointer_cast<SQLStatement>(x);

    BOOL_ptr printSemi = printStatementAfterSemi == nullptr
                         ? stmt->isAfterSemi()
                         : printStatementAfterSemi;

    if ((*printSemi) == BOOL::TRUE)
    {
      print(';');
    }
  }
}

// @Override
bool SQLASTOutputVisitor::visit(SQLArgument_ptr x)
{
  SQLParameter_ParameterType_ptr type = x->getType();
  if (type != nullptr)
  {
    print0(type->name);
    print(' ');
  }

  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCommitStatement_ptr x)
{
  print0(ucase ? "COMMIT" : "commit");

  if (x->isWrite())
  {
    print0(ucase ? " WRITE" : " write");
    if (x->getWait() != nullptr)
    {
      if (x->getWait())
      {
        print0(ucase ? " WAIT" : " wait");
      }
      else
      {
        print0(ucase ? " NOWAIT" : " nowait");
      }
    }

    if (x->getImmediate() != nullptr)
    {
      if (x->getImmediate())
      {
        print0(ucase ? " IMMEDIATE" : " immediate");
      }
      else
      {
        print0(ucase ? " BATCH" : " batch");
      }
    }
  }

  if (x->isWork())
  {
    print0(ucase ? " WORK" : " work");
  }

  if (x->getChain() != nullptr)
  {
    if (x->getChain())
    {
      print0(ucase ? " AND CHAIN" : " and chain");
    }
    else
    {
      print0(ucase ? " AND NO CHAIN" : " and no chain");
    }
  }

  if (x->getRelease() != nullptr)
  {
    if (x->getRelease())
    {
      print0(ucase ? " AND RELEASE" : " and release");
    }
    else
    {
      print0(ucase ? " AND NO RELEASE" : " and no release");
    }
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLFlashbackExpr_ptr x)
{
  print0(x->getType()->name);
  print(' ');
  SQLExpr_ptr expr = x->getExpr();
  if (instanceof <SQLExpr, SQLBinaryOpExpr>(expr))
  {
    print('(');
    expr->accept(SharedObject(SQLASTOutputVisitor));
    print(')');
  }
  else
  {
    expr->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropMaterializedViewStatement_ptr x)
{
  print0(ucase ? "DROP MATERIALIZED VIEW " : "drop materialized view ");
  if (x->isIfExists())
  {
    print0(ucase ? "IF NOT EXISTS " : "if not exists ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowMaterializedViewStatement_ptr x)
{
  print0(ucase ? "SHOW MATERIALIZED VIEWS" : "show materialized views");

  if (x->getLike() != nullptr)
  {
    printUcase(make_string_ptr(" LIKE "));
    x->getLike()->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowCreateMaterializedViewStatement_ptr x)
{
  print0(ucase ? "SHOW CREATE MATERIALIZED VIEW " : "show create materialized view ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLRefreshMaterializedViewStatement_ptr x)
{
  print0(ucase ? "REFRESH MATERIALIZED" : "refresh materialized");

  if (x->isConcurrently())
  {
    print0(ucase ? " CONCURRENTLY" : " concurrently");
  }

  print0(ucase ? " VIEW " : " view ");

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isWithNoData())
  {
    print0(ucase ? " WITH NO DATA" : " with no data");
  }
  else if (x->isWithData())
  {
    print0(ucase ? " WITH DATA" : " with data");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterMaterializedViewStatement_ptr x)
{
  print0(ucase ? "ALTER MATERIALIZED VIEW " : "alter materialized view ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isRefresh())
  {
    println();
    print(ucase ? "REFRESH" : "refresh");

    if (x->isRefreshFast())
    {
      print(ucase ? " FAST" : " fast");
    }
    else if (x->isRefreshComplete())
    {
      print(ucase ? " COMPLETE" : " complete");
    }
    else if (x->isRefreshForce())
    {
      print(ucase ? " FORCE" : " force");
    }

    if (x->isRefreshOnCommit())
    {
      print(ucase ? " ON COMMIT" : " on commit");
    }
    else if (x->isRefreshOnDemand())
    {
      print(ucase ? " ON DEMAND" : " on demand");
    }
    else if (x->isRefreshOnOverWrite())
    {
      print(ucase ? " ON OVERWRITE" : " on overwrite");
    }

    if (x->getStartWith() != nullptr)
    {
      println();
      print(ucase ? "START WITH " : "start with ");
      x->getStartWith()->accept(SharedObject(SQLASTOutputVisitor));
    }

    if (x->getNext() != nullptr)
    {
      print(ucase ? " NEXT " : " next ");
      x->getNext()->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  BOOL_ptr enableQueryRewrite = x->getEnableQueryRewrite();
  // if (enableQueryRewrite != nullptr) {
  println();
  if (enableQueryRewrite)
  {
    print(ucase ? "ENABLE QUERY REWRITE" : "enable query rewrite");
  }
  else
  {
    print(ucase ? "DISABLE QUERY REWRITE" : "disable query rewrite");
  }
  // }

  if (x->isRebuild())
  {
    print0(ucase ? " REBUILD" : " rebuild");
  }

  SQLExpr_list_ptr partitions = x->getPartitions();
  if (!partitions->empty())
  {
    print0(ucase ? "PARTITIONS (" : "partitions (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(partitions);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  return false;
}

// @Override
void SQLASTOutputVisitor::endVisit(SQLAlterMaterializedViewStatement_ptr x)
{
}

bool SQLASTOutputVisitor::visit(SQLCreateMaterializedViewStatement_ptr x)
{
  print0(ucase ? "CREATE MATERIALIZED VIEW " : "create materialized view ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (dbType->name->c_str() == DbType::mysql.name->c_str())
  {
    printTableElements(x->getTableElementList());

    if (x->getDistributedByType() != nullptr)
    {
      println();
      if ((*SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputDistributedLiteralInCreateTableStmt)))) == BOOL::TRUE)
      {
        print0(ucase ? "DISTRIBUTED BY " : "distributed by ");
      }
      else
      {
        print0(ucase ? "DISTRIBUTE BY " : "distribute by ");
      }

      SQLName_ptr distributeByType = x->getDistributedByType();

      if (StringUtils::equalsIgnoreCase(make_string_ptr("HASH"), distributeByType->getSimpleName()))
      {
        print0(ucase ? "HASH(" : "hash(");
        SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getDistributedBy());
        printAndAccept(tmp1, make_string_ptr(","));
        print0(")");
      }
      else if (StringUtils::equalsIgnoreCase(make_string_ptr("DUPLICATE"), distributeByType->getSimpleName()))
      {
        print0(ucase ? "DUPLICATE(" : "duplicate(");
        SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getDistributedBy());
        printAndAccept(tmp1, make_string_ptr(","));
        print0(")");
      }
      else if (StringUtils::equalsIgnoreCase(make_string_ptr("BROADCAST"), distributeByType->getSimpleName()))
      {
        print0(ucase ? "BROADCAST " : "broadcast ");
      }
    }

    for (SQLAssignItem_ptr option : *x->getTableOptions())
    {
      string_ptr key = (std::dynamic_pointer_cast<SQLIdentifierExpr>(option->getTarget()))->getName();

      print(' ');
      print0(ucase ? key : StringUtils::toLower(key));

      if ("TABLESPACE" == key->c_str())
      {
        print(' ');
        option->getValue()->accept(SharedObject(SQLASTOutputVisitor));
        continue;
      }

      print0(" = ");

      option->getValue()->accept(SharedObject(SQLASTOutputVisitor));
    }

    if (x->getComment() != nullptr)
    {
      println();
      print0(ucase ? "COMMENT " : "comment ");
      x->getComment()->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  SQLPartitionBy_ptr partitionBy = x->getPartitionBy();
  if (partitionBy != nullptr)
  {
    println();
    print0(ucase ? "PARTITION BY " : "partition by ");
    partitionBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  this->printOracleSegmentAttributes(x);
  println();

  BOOL_ptr cache = x->getCache();
  // if (cache != nullptr) {
  print(cache ? "CACHE" : "NOCACHE");
  println();
  // }

  BOOL_ptr parallel = x->getParallel();
  // if (parallel != nullptr) {
  if (parallel)
  {
    print(ucase ? "PARALLEL" : "parallel");
    int parallelValue = x->getParallelValue();
    // if (parallelValue != nullptr)
    {
      print(' ');
      print(parallelValue);
    }
  }
  else
  {
    print(ucase ? "NOPARALLEL" : "noparallel");
  }
  println();
  // }

  if (x->isBuildImmediate())
  {
    println(ucase ? "BUILD IMMEDIATE" : "build immediate");
  }

  if (x->isRefresh())
  {
    print(ucase ? "REFRESH" : "refresh");

    if (x->isRefreshFast())
    {
      print(ucase ? " FAST" : " fast");
    }
    else if (x->isRefreshComplete())
    {
      print(ucase ? " COMPLETE" : " complete");
    }
    else if (x->isRefreshForce())
    {
      print(ucase ? " FORCE" : " force");
    }

    if (x->isRefreshOnCommit())
    {
      print(ucase ? " ON COMMIT" : " on commit");
    }
    else if (x->isRefreshOnDemand())
    {
      print(ucase ? " ON DEMAND" : " on demand");
    }
    else if (x->isRefreshOnOverWrite())
    {
      print(ucase ? " ON OVERWRITE" : " on overwrite");
    }

    if (x->getStartWith() != nullptr)
    {
      println();
      print(ucase ? "START WITH " : "start with ");
      x->getStartWith()->accept(SharedObject(SQLASTOutputVisitor));
    }

    if (x->getNext() != nullptr)
    {
      print(ucase ? " NEXT " : " next ");
      x->getNext()->accept(SharedObject(SQLASTOutputVisitor));
    }

    println();
  }

  BOOL_ptr enableQueryRewrite = x->getEnableQueryRewrite();
  // if (enableQueryRewrite != nullptr) {
  if (enableQueryRewrite)
  {
    print(ucase ? "ENABLE QUERY REWRITE" : "enable query rewrite");
  }
  else
  {
    print(ucase ? "DISABLE QUERY REWRITE" : "disable query rewrite");
  }
  println();
  // }

  println(ucase ? "AS" : "as");
  x->getQuery()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLCreateUserStatement_ptr x)
{
  print0(ucase ? "CREATE USER " : "create user ");
  x->getUser()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " IDENTIFIED BY " : " identified by ");
  x->getPassword()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLAlterFunctionStatement_ptr x)
{
  print0(ucase ? "ALTER FUNCTION " : "alter function ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isDebug())
  {
    print0(ucase ? " DEBUG" : " debug");
  }

  if (x->isReuseSettings())
  {
    print0(ucase ? " REUSE SETTINGS" : " reuse settings");
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLAlterTypeStatement_ptr x)
{
  print0(ucase ? "ALTER TYPE " : "alter type ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isCompile())
  {
    print0(ucase ? " COMPILE" : " compile");
  }

  if (x->isBody())
  {
    print0(ucase ? " BODY" : " body");
  }

  if (x->isDebug())
  {
    print0(ucase ? " DEBUG" : " debug");
  }

  if (x->isReuseSettings())
  {
    print0(ucase ? " REUSE SETTINGS" : " reuse settings");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLIntervalExpr_ptr x)
{
  print0(ucase ? "INTERVAL " : "interval ");
  SQLExpr_ptr value = x->getValue();
  value->accept(SharedObject(SQLASTOutputVisitor));

  SQLIntervalUnit_ptr unit = x->getUnit();
  if (unit != nullptr)
  {
    print(' ');
    print0(ucase ? unit->name : unit->nameLCase);
  }
  return false;
}

BOOL_ptr SQLASTOutputVisitor::getPrintStatementAfterSemi()
{
  return printStatementAfterSemi;
}

void SQLASTOutputVisitor::setPrintStatementAfterSemi(BOOL_ptr printStatementAfterSemi)
{
  this->printStatementAfterSemi = printStatementAfterSemi;
}

void SQLASTOutputVisitor::config(VisitorFeature_ptr feature, BOOL_ptr state)
{
  SQLASTVisitorAdapter::config(feature, state);
  if (feature->name->c_str() == VisitorFeature::OutputUCase.name->c_str())
  {
    this->ucase = *state == BOOL::TRUE;
  }
  else if (feature->name->c_str() == VisitorFeature::OutputParameterized.name->c_str())
  {
    this->parameterized = state;
  }
  else if (feature->name->c_str() == VisitorFeature::OutputParameterizedQuesUnMergeInList.name->c_str())
  {
    this->parameterizedQuesUnMergeInList = state;
  }
  else if (feature->name->c_str() == VisitorFeature::OutputParameterizedQuesUnMergeValuesList.name->c_str())
  {
    this->parameterizedQuesUnMergeValuesList = state;
  }
  else if (feature->name->c_str() == VisitorFeature::OutputParameterizedUnMergeShardingTable.name->c_str())
  {
    this->shardingSupport = !state;
    this->parameterizedQuesUnMergeValuesList = state;
  }
  else if (feature->name->c_str() == VisitorFeature::OutputNameQuote.name->c_str())
  {
    this->printNameQuote = state;
  }
}

void SQLASTOutputVisitor::setFeatures(int features)
{
  SQLASTVisitorAdapter::setFeatures(features);
  this->ucase = *(SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputUCase)))) ==BOOL::TRUE;
  this->parameterized = (SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterized))))?BOOL::TRUE:BOOL::FALSE;
  this->parameterizedQuesUnMergeInList = (SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterizedQuesUnMergeInList))))?BOOL::TRUE:BOOL::FALSE;
  this->parameterizedQuesUnMergeValuesList = (SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterizedQuesUnMergeValuesList))))?BOOL::TRUE:BOOL::FALSE;
  this->shardingSupport = !(*SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputParameterizedUnMergeShardingTable))) == BOOL::TRUE);
  this->printNameQuote = (SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputNameQuote))))?BOOL::TRUE:BOOL::FALSE;
}

/////////////// for oracle
BOOL_ptr SQLASTOutputVisitor::visit(OracleCursorExpr_ptr x)
{
  print0(ucase ? "CURSOR(" : "cursor(");
  this->indentCount++;
  println();
  x->getQuery()->accept(SharedObject(SQLASTOutputVisitor));
  this->indentCount--;
  println();
  print(')');
  return BOOL::FALSE;
}

BOOL_ptr SQLASTOutputVisitor::visit(OracleDatetimeExpr_ptr x)
{
  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  SQLExpr_ptr timeZone = x->getTimeZone();

  if (instanceof <SQLExpr, SQLIdentifierExpr>(timeZone))
  {
    if (StringUtils::equalsIgnoreCase((std::dynamic_pointer_cast<SQLIdentifierExpr>(timeZone))->getName(), make_string_ptr("LOCAL")))
    {
      print0(ucase ? " AT LOCAL" : " at local");
      return BOOL::FALSE;
    }
  }

  print0(ucase ? " AT TIME ZONE " : " at time zone ");
  timeZone->accept(SharedObject(SQLASTOutputVisitor));

  return BOOL::FALSE;
}

///////////// for odps & hive
// @Override
bool SQLASTOutputVisitor::visit(SQLLateralViewTableSource_ptr x)
{
  SQLTableSource_ptr tableSource = x->getTableSource();
  if (tableSource != nullptr)
  {
    tableSource->accept(SharedObject(SQLASTOutputVisitor));
  }

  this->indentCount++;
  println();
  print0(ucase ? "LATERAL VIEW " : "lateral view ");

  if (x->isOuter())
  {
    print0(ucase ? "OUTER " : "outer ");
  }

  x->getMethod()->accept(SharedObject(SQLASTOutputVisitor));
  print(' ');
  print0(x->getAlias());

  if (x->getColumns() != nullptr && x->getColumns()->size() > 0)
  {
    print0(ucase ? " AS " : " as ");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getColumns());
    printAndAccept(tmp1, make_string_ptr(", "));
  }

  SQLExpr_ptr on = x->getOn();
  if (on != nullptr)
  {
    println();
    print0(ucase ? "ON " : "on ");
    printExpr(on);
  }

  this->indentCount--;
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowErrorsStatement_ptr x)
{
  print0(ucase ? "SHOW ERRORS" : "show errors");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowRecylebinStatement_ptr x)
{
  print0(ucase ? "SHOW RECYCLEBIN" : "show recyclebin");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowCatalogsStatement_ptr x)
{
  print0(ucase ? "SHOW CATALOGS" : "show catalogs");
  SQLExpr_ptr like = x->getLike();
  if (like != nullptr)
  {
    print0(ucase ? " LIKE " : " like ");
    printExpr(like);
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowFunctionsStatement_ptr x)
{
  print0(ucase ? "SHOW FUNCTIONS" : "show functions");
  SQLExpr_ptr like = x->getLike();
  if (like != nullptr)
  {
    print0(ucase ? " LIKE " : " like ");
    printExpr(like);
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowSessionStatement_ptr x)
{
  print0(ucase ? "SHOW SESSION" : "show session");
  SQLExpr_ptr like = x->getLike();
  if (like != nullptr)
  {
    print0(ucase ? " LIKE " : " like ");
    printExpr(like);
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterCharacter_ptr x)
{
  if (x->getCharacterSet() != nullptr)
  {
    print0(ucase ? "CHARACTER SET = " : "character set = ");
    x->getCharacterSet()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getCollate() != nullptr)
  {
    if (x->getCharacterSet() != nullptr)
    {
      print0(", ");
    }
    print0(ucase ? "COLLATE = " : "collate = ");
    x->getCollate()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLRecordDataType_ptr x)
{
  print0(ucase ? "RECORD (" : "record (");
  indentCount++;
  println();
  SQLColumnDefinition_list_ptr columns = x->getColumns();
  auto it_columns = columns->begin();
  for (int i = 0; i < columns->size(); i++)
  {
    if (i != 0)
    {
      println();
    }
    std::advance(it_columns, i);
    (*it_columns)->accept(SharedObject(SQLASTOutputVisitor));
    if (i != columns->size() - 1)
    {
      print0(", ");
    }
  }
  indentCount--;
  println();
  print(')');

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLExprStatement_ptr x)
{
  x->getExpr()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLBlockStatement_ptr x)
{
  if (!x->getParameters()->empty())
  {
    this->indentCount++;
    if (instanceof <SQLObject, SQLCreateProcedureStatement>(x->getParent()))
    {
      SQLCreateProcedureStatement_ptr procedureStatement = std::dynamic_pointer_cast<SQLCreateProcedureStatement>(x->getParent());
      if ((*procedureStatement->isCreate())==BOOL::TRUE)
      {
        printIndent();
      }
    }
    if (!(instanceof <SQLObject, SQLCreateProcedureStatement>(x->getParent()) ||
          instanceof <SQLObject, SQLCreateFunctionStatement>(x->getParent()) ||
          instanceof <SQLObject, OracleFunctionDataType>(x->getParent()) ||
          instanceof <SQLObject, OracleProcedureDataType>(x->getParent())))
    {
      print0(ucase ? "DECLARE" : "declare");
      println();
    }

    auto it_getParameters = x->getParameters()->begin();
    for (int i = 0, size = x->getParameters()->size(); i < size; ++i)
    {
      if (i != 0)
      {
        println();
      }
      std::advance(it_getParameters, i);
      SQLParameter_ptr param = *it_getParameters;
      param->accept(SharedObject(SQLASTOutputVisitor));
      print(';');
    }

    this->indentCount--;
    println();
  }
  print0(ucase ? "BEGIN" : "begin");
  this->indentCount++;

  auto it_getStatementList = x->getStatementList()->begin();
  for (int i = 0, size = x->getStatementList()->size(); i < size; ++i)
  {
    println();
    std::advance(it_getStatementList, i);
    SQLStatement_ptr stmt = *it_getStatementList;
    stmt->accept(SharedObject(SQLASTOutputVisitor));
  }
  this->indentCount--;

  SQLStatement_ptr exception = x->getException();
  if (exception != nullptr)
  {
    println();
    exception->accept(SharedObject(SQLASTOutputVisitor));
  }

  println();
  print0(ucase ? "END;" : "end;");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCreateProcedureStatement_ptr x)
{
  BOOL_ptr create = x->isCreate();
  if (!create)
  {
    print0(ucase ? "PROCEDURE " : "procedure ");
  }
  else if (x->isOrReplace())
  {
    print0(ucase ? "CREATE OR REPLACE PROCEDURE " : "create or replace procedure ");
  }
  else
  {
    print0(ucase ? "CREATE PROCEDURE " : "create procedure ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  int paramSize = x->getParameters()->size();

  if (paramSize > 0)
  {
    print0(" (");
    this->indentCount++;
    println();

    auto it_getParameters = x->getParameters()->begin();
    for (int i = 0; i < paramSize; ++i)
    {
      if (i != 0)
      {
        print0(", ");
        println();
      }
      std::advance(it_getParameters, i);
      SQLParameter_ptr param = *it_getParameters;
      param->accept(SharedObject(SQLASTOutputVisitor));
    }

    this->indentCount--;
    println();
    print(')');
  }

  SQLName_ptr authid = x->getAuthid();
  if (authid != nullptr)
  {
    print(ucase ? " AUTHID " : " authid ");
    authid->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLStatement_ptr block = x->getBlock();
  string_ptr wrappedSource = x->getWrappedSource();
  if (wrappedSource != nullptr)
  {
    print0(ucase ? " WRAPPED " : " wrapped ");
    print0(wrappedSource);
  }
  else
  {
    if (block != nullptr && !create)
    {
      println();
      print("IS");
      println();
    }
    else
    {
      println();
      if (instanceof <SQLStatement, SQLBlockStatement>(block))
      {
        SQLBlockStatement_ptr blockStatement = std::dynamic_pointer_cast<SQLBlockStatement>(block);
        if (blockStatement->getParameters()->size() > 0 || authid != nullptr)
        {
          println(ucase ? "AS" : "as");
        }
        else
        {
          println(ucase ? "IS" : "is");
        }
      }
    }

    string_ptr javaCallSpec = x->getJavaCallSpec();
    if (javaCallSpec != nullptr)
    {
      print0(ucase ? "LANGUAGE JAVA NAME '" : "language java name '");
      print0(javaCallSpec);
      print('\'');
      return false;
    }
  }

  BOOL_ptr afterSemi = BOOL::FALSE;
  if (block != nullptr)
  {
    block->accept(SharedObject(SQLASTOutputVisitor));

    if (instanceof <SQLStatement, SQLBlockStatement>(block) &&
                       (std::dynamic_pointer_cast<SQLBlockStatement>(block))->getStatementList()->size() > 0)
    {
      auto it_getStatementList = (std::dynamic_pointer_cast<SQLBlockStatement>(block))->getStatementList()->begin();
      afterSemi = (*it_getStatementList)->isAfterSemi();
    }
  }

  if ((!afterSemi) && instanceof <SQLObject, OracleCreatePackageStatement>(x->getParent()))
  {
    print(';');
  }
  return false;
}

BOOL_ptr SQLASTOutputVisitor::hiveVisit(SQLExternalRecordFormat_ptr x)
{
  indentCount++;
  if (x->getDelimitedBy() != nullptr)
  {
    println();
    print0(ucase ? "LINES TERMINATED BY " : "lines terminated by ");
    x->getDelimitedBy()->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr terminatedBy = x->getTerminatedBy();
  if (terminatedBy != nullptr)
  {
    println();
    print0(ucase ? "FIELDS TERMINATED BY " : "fields terminated by ");
    terminatedBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr escapedBy = x->getEscapedBy();
  if (escapedBy != nullptr)
  {
    println();
    print0(ucase ? "ESCAPED BY " : "escaped by ");
    escapedBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr collectionItemsTerminatedBy = x->getCollectionItemsTerminatedBy();
  if (collectionItemsTerminatedBy != nullptr)
  {
    println();
    print0(ucase ? "COLLECTION ITEMS TERMINATED BY " : "collection items terminated by ");
    collectionItemsTerminatedBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr mapKeysTerminatedBy = x->getMapKeysTerminatedBy();
  if (mapKeysTerminatedBy != nullptr)
  {
    println();
    print0(ucase ? "MAP KEYS TERMINATED BY " : "map keys terminated by ");
    mapKeysTerminatedBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr linesTerminatedBy = x->getLinesTerminatedBy();
  if (linesTerminatedBy != nullptr)
  {
    println();
    print0(ucase ? "LINES TERMINATED BY " : "lines terminated by ");
    linesTerminatedBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr nullDefinedAs = x->getNullDefinedAs();
  if (nullDefinedAs != nullptr)
  {
    println();
    print0(ucase ? "NULL DEFINED AS " : "nullptr defined as ");
    nullDefinedAs->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr serde = x->getSerde();
  if (serde != nullptr)
  {
    println();
    print0(ucase ? "SERDE " : "serde ");
    serde->accept(SharedObject(SQLASTOutputVisitor));
  }

  indentCount--;

  return BOOL::FALSE;
}

bool SQLASTOutputVisitor::visit(SQLExternalRecordFormat_ptr x)
{
  SQLExpr_ptr delimitedBy = x->getDelimitedBy();
  if (delimitedBy != nullptr)
  {
    println();
    print0(ucase ? "RECORDS DELIMITED BY " : "records delimited by ");
    delimitedBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  BOOL_ptr logfile = x->getLogfile();
  // if (logfile != nullptr) {
  if (logfile)
  {
    print0(ucase ? " LOGFILE" : " logfile");
  }
  else
  {
    print0(ucase ? " NOLOGFILE" : " nologfile");
  }
  // }

  BOOL_ptr badfile = x->getBadfile();
  // if (badfile != nullptr) {
  if (badfile)
  {
    print0(ucase ? " BADFILE" : " badfile");
  }
  else
  {
    print0(ucase ? " NOBADFILE" : " nobadfile");
  }
  // }

  SQLExpr_ptr terminatedBy = x->getTerminatedBy();
  if (terminatedBy != nullptr)
  {
    println();
    print0(ucase ? "FIELDS TERMINATED BY " : "fields terminated by ");
    terminatedBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isLtrim())
  {
    print0(ucase ? " LTRIM" : " ltrim");
  }

  if (x->isMissingFieldValuesAreNull())
  {
    print0(ucase ? " MISSING FIELD VALUES ARE NULL" : " missing field values are nullptr");
  }

  if (x->isRejectRowsWithAllNullFields())
  {
    print0(ucase ? " REJECT ROWS WITH ALL NULL FIELDS" : " reject rows with all nullptr fields");
  }

  return false;
}

// @Override
BOOL_ptr SQLASTOutputVisitor::visit(SQLArrayDataType_ptr x)
{
  SQLExpr_list_ptr arguments = x->getArguments();
  uint64_t status = 99;
  bool tmp = x->getAttribute("ads.arrayDataType")->getBool();
  if (tmp)
  {
    x->getComponentType()->accept(SharedObject(SQLASTOutputVisitor));
    print('[');
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(arguments);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(']');
  }
  else
  {
    SQLDataType_ptr componentType = x->getComponentType();
    if (componentType != nullptr)
    {
      print0(ucase ? "ARRAY<" : "array<");
      componentType->accept(SharedObject(SQLASTOutputVisitor));
      print('>');
    }
    else
    {
      print0(ucase ? "ARRAY" : "array");
    }

    if (arguments->size() > 0)
    {
      print('(');
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(arguments);
      printAndAccept(tmp1, make_string_ptr(", "));
      print(')');
    }
  }
  return BOOL::FALSE;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLMapDataType_ptr x)
{
  print0(ucase ? "MAP<" : "map<");

  SQLDataType_ptr keyType = x->getKeyType();
  SQLDataType_ptr valueType = x->getValueType();

  keyType->accept(SharedObject(SQLASTOutputVisitor));
  print0(", ");

  valueType->accept(SharedObject(SQLASTOutputVisitor));
  print('>');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLStructDataType_ptr x)
{
  print0(ucase ? "STRUCT<" : "struct<");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLStructDataType_Field, SQLObject>(x->getFields());
  printAndAccept(tmp1, make_string_ptr(", "));
  print('>');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLRowDataType_ptr x)
{
  print0(ucase ? "ROW(" : "row(");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLStructDataType_Field, SQLObject>(x->getFields());
  printAndAccept(tmp1, make_string_ptr(","));
  print(')');
  return false;
}

// @Override
BOOL_ptr SQLASTOutputVisitor::visit(SQLUnionDataType_ptr x)
{
  print0(ucase ? "UNIONTYPE<" : "uniontype<");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLDataType, SQLObject>(x->getItems());
  printAndAccept(tmp1, make_string_ptr(", "));
  print('>');
  return BOOL::FALSE;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLStructDataType_Field_ptr x)
{
  SQLName_ptr  name = x->getName();
  if (name != nullptr)
  {
    name->accept(SharedObject(SQLASTOutputVisitor));
  }
  SQLDataType_ptr dataType = x->getDataType();

  if (dataType != nullptr)
  {
    if (instanceof <SQLObject, SQLRowDataType>(x->getParent()))
    {
      if (name != nullptr)
      {
        print(' ');
      }
    }
    else
    {
      print(':');
    }

    dataType->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLAlterTableMergePartition_ptr x)
{
  print0(ucase ? "MERGE " : "merge ");
  if (*(x->isIfExists()) == BOOL::TRUE)
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }
  println();
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLPartitionSpec, SQLObject>(x->getPartitions());
  printlnAndAccept(tmp1, make_string_ptr(", "));
  println();
  print0(ucase ? "OVERWRITE" : "overwrite");
  println();
  x->getOverwritePartition()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLPartitionSpec_ptr x)
{
  print0(ucase ? "PARTITION (" : "partition (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLPartitionSpec_Item, SQLObject>(x->getItems());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');
  return false;
}

bool SQLASTOutputVisitor::visit(SQLPartitionSpec_Item_ptr x)
{
  x->getColumn()->accept(SharedObject(SQLASTOutputVisitor));
  print0(" = ");
  x->getValue()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLAlterTableSubpartitionAvailablePartitionNum_ptr x)
{
  print0(ucase ? "SUBPARTITION_AVAILABLE_PARTITION_NUM = " : "subpartition_available_partition_num = ");
  x->getNumber()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowDatabasesStatement_ptr x)
{
  print0(ucase ? "SHOW " : "show ");
  if (x->isFull())
  {
    print0(ucase ? "FULL " : "full ");
  }
  if (x->isPhysical())
  {
    print0(ucase ? "PHYSICAL " : "physical ");
  }
  print0(ucase ? "DATABASES" : "databases");

  if (x->getLike() != nullptr)
  {
    print0(ucase ? " LIKE " : " like ");
    x->getLike()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getWhere() != nullptr)
  {
    print0(ucase ? " WHERE " : " where ");
    x->getWhere()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isExtra())
  {
    print0(ucase ? " EXTRA" : " extra");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowTableGroupsStatement_ptr x)
{
  print0(ucase ? "SHOW TABLEGROUPS" : "show tablegroups");

  if (x->getDatabase() != nullptr)
  {
    print0(ucase ? " IN " : " in ");
    x->getDatabase()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowColumnsStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
      println();
    }
  }

  if (x->isFull())
  {
    print0(ucase ? "SHOW FULL COLUMNS" : "show full columns");
  }
  else
  {
    print0(ucase ? "SHOW COLUMNS" : "show columns");
  }

  if (x->getTable() != nullptr)
  {
    print0(ucase ? " FROM " : " from ");
    if (x->getDatabase() != nullptr)
    {
      x->getDatabase()->accept(SharedObject(SQLASTOutputVisitor));
      print('.');
    }
    x->getTable()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getLike() != nullptr)
  {
    print0(ucase ? " LIKE " : " like ");
    x->getLike()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getWhere() != nullptr)
  {
    print0(ucase ? " WHERE " : " where ");
    x->getWhere()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowCreateTableStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
      println();
    }
  }

  if (x->isAll())
  {
    print0(ucase ? "SHOW ALL CREATE TABLE " : "show all create table ");
  }
  else
  {
    print0(ucase ? "SHOW CREATE TABLE " : "show create table ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getLikeMapping() != nullptr)
  {
    print0(ucase ? " LIKE MAPPING (" : " like mapping ( ");
    x->getLikeMapping()->accept(SharedObject(SQLASTOutputVisitor));
    print0(")");
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowProcessListStatement_ptr x)
{
  print0(ucase ? "SHOW " : "show ");

  if (x->isFull())
  {
    print0(ucase ? "FULL " : "full ");
  }

  print0(ucase ? "PROCESSLIST" : "processlist");

  if (x->isMpp())
  {
    print0(ucase ? " MPP" : " mpp");
  }

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    print0(ucase ? " WHERE " : " where ");
    where->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLOrderBy_ptr orderBy = x->getOrderBy();
  if (orderBy != nullptr)
  {
    print(' ');
    orderBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLLimit_ptr limit = x->getLimit();
  if (limit != nullptr)
  {
    print(' ');
    limit->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableSetOption_ptr x)
{
  print0(ucase ? "SET TBLPROPERTIES (" : "set tblproperties (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getOptions());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  SQLExpr_ptr on = x->getOn();
  if (on != nullptr)
  {
    print0(ucase ? " ON " : " on ");
    on->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowCreateViewStatement_ptr x)
{
  print0(ucase ? "SHOW CREATE VIEW " : "show create view ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowViewsStatement_ptr x)
{
  print0(ucase ? "SHOW VIEWS" : "show views");
  if (x->getDatabase() != nullptr)
  {
    print0(ucase ? " FROM " : " from ");
    x->getDatabase()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getLike() != nullptr)
  {
    print0(ucase ? " LIKE " : " like ");
    x->getLike()->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLAlterTableRenameIndex_ptr x)
{
  print0(ucase ? "RENAME INDEX " : "rename index ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " TO " : " to ");
  x->getTo()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLCreateRoleStatement_ptr x)
{
  print0(ucase ? "CREATE ROLE " : "create role ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLDropRoleStatement_ptr x)
{
  print0(ucase ? "DROP ROLE " : "drop role ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
void SQLASTOutputVisitor::endVisit(SQLMatchAgainstExpr_ptr x)
{
}

// @Override
bool SQLASTOutputVisitor::visit(SQLMatchAgainstExpr_ptr x)
{
  print0(ucase ? "MATCH (" : "match (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getColumns());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');

  print0(ucase ? " AGAINST (" : " against (");
  x->getAgainst()->accept(SharedObject(SQLASTOutputVisitor));
  if (x->getSearchModifier() != nullptr)
  {
    print(' ');
    print0(ucase ? x->getSearchModifier()->name : x->getSearchModifier()->nameLCase);
  }
  print(')');

  return false;
}

BOOL_ptr SQLASTOutputVisitor::visit(MySqlPrimaryKey_ptr x)
{
  visit(x->getIndexDefinition());
  /*
  if (x->getName() != nullptr) {
      print0(ucase ? "CONSTRAINT " : "constraint ");
      x->getName()->accept(SharedObject(SQLASTOutputVisitor));
      print(' ');
  }

  print0(ucase ? "PRIMARY KEY" : "primary key");

  if (x->getIndexType() != nullptr) {
      print0(ucase ? " USING " : " using_ ");
      print0(x->getIndexType());
  }

  print0(" (");

  for (int i = 0, size = x->getColumns()->size(); i < size; ++i) {
      if (i != 0) {
          print0(", ");
      }
      x->getColumns().get(i)->accept(SharedObject(SQLASTOutputVisitor));
  }
  print(')');

  SQLExpr_ptr keyBlockSize = x->getKeyBlockSize();
  if (keyBlockSize != nullptr) {
      print0(ucase ? " KEY_BLOCK_SIZE = " : " key_block_size = ");
      keyBlockSize->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr comment = x->getComment();
  if (comment != nullptr) {
      print0(ucase ? " COMMENT " : " comment ");
      comment->accept(SharedObject(SQLASTOutputVisitor));
  }
  */

  return BOOL::FALSE;
}

BOOL_ptr SQLASTOutputVisitor::visit(MySqlCreateTableStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
      println();
    }
  }

  if (isPrettyFormat() && x->hasBeforeComment())
  {
    printlnComments(x->getBeforeCommentsDirect());
  }

  print0(ucase ? "CREATE " : "create ");

  for (SQLCommentHint_ptr hint : *x->getHints())
  {
    hint->accept(SharedObject(SQLASTOutputVisitor));
    print(' ');
  }

  if (x->isDimension())
  {
    print0(ucase ? "DIMENSION " : "dimension ");
  }

  if (SQLCreateTableStatement::Type::GLOBAL_TEMPORARY == x->getType())
  {
    print0(ucase ? "TEMPORARY TABLE " : "temporary table ");
  }
  else if (SQLCreateTableStatement::Type::SHADOW == x->getType())
  {
    print0(ucase ? "SHADOW TABLE " : "shadow table ");
  }
  else if (x->isExternal())
  {
    print0(ucase ? "EXTERNAL TABLE " : "external table ");
  }
  else
  {
    print0(ucase ? "TABLE " : "table ");
  }

  if (x->isIfNotExists())
  {
    print0(ucase ? "IF NOT EXISTS " : "if not exists ");
  }

  printTableSourceExpr(x->getName());

  if (x->getLike() != nullptr)
  {
    print0(ucase ? " LIKE " : " like ");
    x->getLike()->accept(SharedObject(SQLASTOutputVisitor));
  }

  printTableElements(x->getTableElementList());

  if (x->isBroadCast())
  {
    print0(ucase ? " BROADCAST" : " broadcast");
  }

  SQLAssignItem_list_ptr tableOptions = x->getTableOptions();
  bool tmp = x->getAttribute( "ads.options")->getBool();
  if (tmp)
  {
    if (tableOptions->size() > 0)
    {
      println();
      print0(ucase ? "OPTIONS (" : "options (");
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(tableOptions);
      printAndAccept(tmp1, make_string_ptr(", "));
      print(')');
    }
  }
  else
  {
    for (SQLAssignItem_ptr option : *tableOptions)
    {
      string_ptr key = (std::dynamic_pointer_cast<SQLIdentifierExpr>(option->getTarget()))->getName();

      print(' ');
      print0(ucase ? key : StringUtils::toLower(key));

      if ("TABLESPACE" == key->c_str())
      {
        print(' ');
        option->getValue()->accept(SharedObject(SQLASTOutputVisitor));
        continue;
      }

      print0(" = ");

      option->getValue()->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  SQLExpr_ptr comment = x->getComment();
  if (comment != nullptr)
  {
    print0(ucase ? " COMMENT " : " comment ");
    comment->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getDistributeByType() != nullptr)
  {
    println();
    if (*(SQLASTVisitorAdapter::isEnabled(VisitorFeature_ptr(new VisitorFeature(&VisitorFeature::OutputDistributedLiteralInCreateTableStmt)))) == BOOL::TRUE)
    {
      print0(ucase ? "DISTRIBUTED BY " : "distributed by ");
    }
    else
    {
      print0(ucase ? "DISTRIBUTE BY " : "distribute by ");
    }

    SQLName_ptr distributeByType = x->getDistributeByType();

    if (StringUtils::equalsIgnoreCase(make_string_ptr("HASH"),distributeByType->getSimpleName()))
    {
      print0(ucase ? "HASH(" : "hash(");
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getDistributeBy());
      printAndAccept(tmp1, make_string_ptr(","));
      print0(")");
    }
    else if (StringUtils::equalsIgnoreCase(make_string_ptr("DUPLICATE"),distributeByType->getSimpleName()))
    {
      print0(ucase ? "DUPLICATE(" : "duplicate(");
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(x->getDistributeBy());
      printAndAccept(tmp1, make_string_ptr(","));
      print0(")");
    }
    else if (StringUtils::equalsIgnoreCase(make_string_ptr("BROADCAST"),distributeByType->getSimpleName()))
    {
      print0(ucase ? "BROADCAST " : "broadcast ");
    }
  }

  SQLPartitionBy_ptr partitionBy = x->getPartitioning();
  if (partitionBy != nullptr)
  {
    println();
    print0(ucase ? "PARTITION BY " : "partition by ");
    partitionBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLSelectOrderByItem_list_ptr clusteredBy = x->getClusteredBy();
  if (clusteredBy->size() > 0)
  {
    println();
    print0(ucase ? "CLUSTERED BY (" : "clustered by (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(clusteredBy);
    printAndAccept(tmp1, make_string_ptr(","));
    print0(")");
  }

  SQLExpr_ptr dbPartitionBy = x->getDbPartitionBy();
  if (dbPartitionBy != nullptr)
  {
    println();
    print0(ucase ? "DBPARTITION BY " : "dbpartition by ");
    dbPartitionBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr dbpartitions = x->getDbpartitions();
  if (dbpartitions != nullptr)
  {
    print0(ucase ? " DBPARTITIONS " : " dbpartitions ");
    dbpartitions->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr tbPartitionsBy = x->getTablePartitionBy();
  if (tbPartitionsBy != nullptr)
  {
    println();
    print0(ucase ? "TBPARTITION BY " : "tbpartition by ");
    tbPartitionsBy->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr tablePartitions = x->getTablePartitions();
  if (tablePartitions != nullptr)
  {
    print0(ucase ? " TBPARTITIONS " : " tbpartitions ");
    tablePartitions->accept(SharedObject(SQLASTOutputVisitor));
  }

  MySqlExtPartition_ptr extPartition = x->getExtPartition();
  if (extPartition != nullptr)
  {
    println();
    extPartition->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getArchiveBy() != nullptr)
  {
    println();
    print0(ucase ? "ARCHIVE BY = " : "archive by = ");
    x->getArchiveBy()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getTableGroup() != nullptr)
  {
    println();
    print0(ucase ? "TABLEGROUP " : "tablegroup ");
    x->getTableGroup()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isReplace())
  {
    println();
    print0(ucase ? "REPLACE " : "replace ");
  }
  else if (x->isIgnore())
  {
    println();
    print0(ucase ? "IGNORE " : "ignore ");
  }

  if (x->getSelect() != nullptr)
  {
    println();
    print0(ucase ? "AS" : "as");
    println();
    x->getSelect()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getStoredBy() != nullptr)
  {
    println();
    print0(ucase ? " STORED BY " : " stored by ");
    x->getStoredBy()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getWith()->size() > 0)
  {
    println();
    print0(ucase ? " WITH (" : " with (");
    int i = 0;
    for (auto option : *x->getWith())
    {
      if (i != 0)
      {
        print0(", ");
      }
      print0(option.first);
      print0(" = ");
      option.second->accept(SharedObject(SQLASTOutputVisitor));
      i++;
    }
    print(')');
  }

  if (x->getWithData() != nullptr)
  {
    println();
    if (x->getWithData())
    {
      print0(ucase ? "WITH DATA" : "with data");
    }
    else
    {
      print0(ucase ? "WITH NO DATA" : "with no data");
    }
  }

  for (SQLCommentHint_ptr hint : *x->getOptionHints())
  {
    print(' ');
    hint->accept(SharedObject(SQLASTOutputVisitor));
  }
  return BOOL::FALSE;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowPartitionsStmt_ptr x)
{
  print0(ucase ? "SHOW PARTITIONS " : "show partitions ");
  x->getTableSource()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getPartition()->size() > 0)
  {
    print0(ucase ? " PARTITION (" : " partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartition());
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(")");
  }

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    print0(ucase ? " WHERE " : " where ");
    where->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLValuesExpr_ptr x)
{
  print0(ucase ? "VALUES (" : "values (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLListExpr, SQLObject>(x->getValues());
  printAndAccept(tmp1, make_string_ptr(", "));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDumpStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
      println();
    }
  }

  print0(ucase ? "DUMP DATA " : "dump data ");

  if (x->isOverwrite())
  {
    print0(ucase ? "OVERWRITE " : "overwrite ");
  }

  SQLExprTableSource_ptr into = x->getInto();
  if (into != nullptr)
  {
    print0(ucase ? "INTO " : "into ");
    into->accept(SharedObject(SQLASTOutputVisitor));
    print0(" ");
  }

  x->getSelect()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLValuesTableSource_ptr x)
{
  SQLName_list_ptr columns = x->getColumns();

  BOOL_ptr quote = (columns->size() > 0)?BOOL::TRUE:BOOL::FALSE;
  if (quote)
  {
    print('(');
  }
  print0(ucase ? "VALUES " : "values ");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLListExpr, SQLObject>(x->getValues());
  printAndAccept(tmp1, make_string_ptr(", "));

  if (quote)
  {
    print(')');
  }

  string_ptr alias = x->getAlias();
  if (alias != nullptr)
  {
    print0(ucase ? " AS " : " as ");
    print0(alias);
  }

  if (columns->size() > 0)
  {
    if (alias == nullptr)
    {
      print0(ucase ? " AS" : " as");
    }
    print0(" (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(columns);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLExtractExpr_ptr x)
{
  print0(ucase ? "EXTRACT(" : "extract(");
  print0(x->getUnit()->name);
  print0(ucase ? " FROM " : " from ");
  x->getValue()->accept(SharedObject(SQLASTOutputVisitor));
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLWindow_ptr x)
{
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " AS " : " as ");
  x->getOver()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLJSONExpr_ptr x)
{
  print0(ucase ? "JSON " : "json ");
  printChars(x->getLiteral());
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAnnIndex_ptr x)
{
  print0(ucase ? "ANNINDX (type = '" : "annindx (type = '");

  int indexType = x->getIndexType();
  if ((indexType & SQLAnnIndex_IndexType::Flat.mask) != 0)
  {
    print0(ucase ? "FLAT" : "flat");
  }

  if ((indexType & SQLAnnIndex_IndexType::FastIndex.mask) != 0)
  {
    if (indexType != SQLAnnIndex_IndexType::FastIndex.mask)
    {
      print(',');
    }
    print0(ucase ? "FLAT_INDEX" : "flat_index");
  }

  print0(ucase ? "', DISTANCE = '" : "', distance = '");
  print0(x->getDistance()->name);
  print0("'");

  int rtIndexType = x->getRtIndexType();
  if (rtIndexType != 0)
  {
    print0(ucase ? ", RTTYPE = '" : ", rttype = '");

    if ((rtIndexType & SQLAnnIndex_IndexType::Flat.mask) != 0)
    {
      print0(ucase ? "FLAT" : "flat");
    }

    if ((rtIndexType & SQLAnnIndex_IndexType::FastIndex.mask) != 0)
    {
      if (rtIndexType != SQLAnnIndex_IndexType::FastIndex.mask)
      {
        print(',');
      }
      print0(ucase ? "FLAT_INDEX " : "flat_index ");
    }

    print('\'');
  }

  print(')');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLAlterTableRecoverPartitions_ptr x)
{
  print0(ucase ? "RECOVER PARTITIONS" : "recover partitions");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterIndexStatement_ptr x)
{
  print0(ucase ? "ALTER INDEX " : "alter index ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getRenameTo() != nullptr)
  {
    print0(ucase ? " RENAME TO " : " rename to ");
    x->getRenameTo()->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExprTableSource_ptr table = x->getTable();
  if (table != nullptr)
  {
    print0(ucase ? " ON " : " on ");
    table->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getPartitions()->size() > 0)
  {
    print0(ucase ? " PARTITION (" : " partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartitions());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  if (x->isCompile())
  {
    print0(ucase ? " COMPILE" : " compile");
  }

  if (x->getEnable() == BOOL::TRUE)
  {
    print0(ucase ? " ENABLE" : " enable");
  }
  if (x->getEnable() == BOOL::FALSE)
  {
    print0(ucase ? " DISABLE" : " disable");
  }

  if (x->isUnusable())
  {
    print0(ucase ? " UNUSABLE" : " unusable");
  }

  if (x->getMonitoringUsage() != nullptr)
  {
    print0(ucase ? " MONITORING USAGE" : " monitoring usage");
  }

  if (x->getRebuild() != nullptr)
  {
    print(' ');
    x->getRebuild()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getParallel() != nullptr)
  {
    print0(ucase ? " PARALLEL" : " parallel");
    x->getParallel()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterIndexStatement_Rebuild_ptr x)
{
  print0(ucase ? "REBUILD" : "rebuild");

  if (x->getOption() != nullptr)
  {
    print(' ');
    x->getOption()->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowIndexesStatement_ptr x)
{
  SQLCommentHint_list_ptr headHints = x->getHeadHintsDirect();
  if (headHints != nullptr)
  {
    for (SQLCommentHint_ptr hint : *headHints)
    {
      hint->accept(SharedObject(SQLASTOutputVisitor));
      println();
    }
  }

  print0(ucase ? "SHOW " : "show ");
  print0(ucase ? StringUtils::toUpper(x->getType()) : StringUtils::toLower(x->getType()));

  SQLExprTableSource_ptr table = x->getTable();
  if (table != nullptr)
  {
    print0(ucase ? " FROM " : " from ");
    x->getTable()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getHints() != nullptr && x->getHints()->size() > 0)
  {
    print(' ');
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLCommentHint, SQLObject>(x->getHints());
    printAndAccept(tmp1, make_string_ptr(" "));
  }

  if (x->getWhere() != nullptr)
  {
    print0(ucase ? " WHERE " : " where ");
    x->getWhere()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAnalyzeTableStatement_ptr x)
{
  print0(ucase ? "ANALYZE TABLE " : "analyze table ");

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExprTableSource, SQLObject>(x->getTables());
  printAndAccept(tmp1, make_string_ptr(", "));

  SQLPartitionRef_ptr partition = x->getPartition();
  if (partition != nullptr)
  {
    print(' ');
    partition->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isComputeStatistics())
  {
    print0(ucase ? " COMPUTE STATISTICS" : " compute statistics");
  }

  if (x->isForColums())
  {
    print0(ucase ? " FOR COLUMNS" : " for columns");
  }

  if (x->isCacheMetadata())
  {
    print0(ucase ? " CACHE METADATA" : " cache metadata");
  }

  if (x->isNoscan())
  {
    print0(ucase ? " NOSCAN" : " noscan");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPartitionRef_ptr x)
{
  print0(ucase ? "PARTITION (" : "partition (");
  auto it_getItems = x->getItems()->begin();
  for (int i = 0; i < x->getItems()->size(); i++)
  {
    if (i != 0)
    {
      print(", ");
    }
    std::advance(it_getItems,i);
    SQLPartitionRef_Item_ptr item = *it_getItems;
    visit(item);
  }
  print(')');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLPartitionRef_Item_ptr x)
{
  visit(x->getColumnName());

  SQLExpr_ptr value = x->getValue();
  if (value != nullptr)
  {
    SQLBinaryOperator_ptr operator_ = x->getOperator();
    if (operator_ == nullptr)
    {
      print(" = ");
    }
    else
    {
      printOperator(operator_);
    }

    printExpr(value);
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLExportTableStatement_ptr x)
{
  print0(ucase ? "EXPORT TABLE " : "export table ");
  x->getTable()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getPartition()->size() > 0)
  {
    print0(ucase ? " PARTITION (" : " partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartition());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  SQLExpr_ptr to = x->getTo();
  if (to != nullptr)
  {
    print0(ucase ? " TO " : " to ");
    to->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

bool SQLASTOutputVisitor::visit(SQLImportTableStatement_ptr x)
{
  if (x->isExtenal())
  {
    print0(ucase ? "IMPORT EXTERNAL" : "import external");
  }
  else
  {
    print0(ucase ? "IMPORT" : "import");
  }

  SQLExprTableSource_ptr table = x->getTable();
  if (table != nullptr)
  {
    print0(ucase ? " TABLE " : " table ");
    table->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getPartition()->size() > 0)
  {
    print0(ucase ? " PARTITION (" : " partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartition());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  SQLExpr_ptr from = x->getFrom();
  if (from != nullptr)
  {
    print0(ucase ? " FROM " : " from ");
    from->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr location = x->getLocation();
  if (location != nullptr)
  {
    print0(ucase ? " LOCATION " : " location ");
    location->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getVersion() != nullptr)
  {
    print0(ucase ? " VERSIOIN = " : " version = ");
    x->getVersion()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isUsingBuild())
  {
    print0(ucase ? " BUILD = 'Y'" : " build = 'y'");
  }

  return false;
}

bool SQLASTOutputVisitor::visit(SQLTableSampling_ptr x)
{
  print0(ucase ? "TABLESAMPLE " : "tablesample ");

  if (x->isBernoulli())
  {
    print0(ucase ? "BERNOULLI " : "bernoulli ");
  }
  else if (x->isSystem())
  {
    print0(ucase ? "SYSTEM " : "system ");
  }

  print('(');

  SQLExpr_ptr bucket = x->getBucket();
  if (bucket != nullptr)
  {
    print0(ucase ? "BUCKET " : "bucket ");
    bucket->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr outOf = x->getOutOf();
  if (outOf != nullptr)
  {
    print0(ucase ? " OUT OF " : " out of ");
    outOf->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr on = x->getOn();
  if (on != nullptr)
  {
    print0(ucase ? " ON " : " on ");
    on->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr percent = x->getPercent();
  if (percent != nullptr)
  {
    percent->accept(SharedObject(SQLASTOutputVisitor));
    print0(ucase ? " PERCENT" : " percent");
  }

  SQLExpr_ptr rows = x->getRows();
  if (rows != nullptr)
  {
    rows->accept(SharedObject(SQLASTOutputVisitor));

    if (dbType->name->c_str() != DbType::mysql.name->c_str())
    {
      print0(ucase ? " ROWS" : " rows");
    }
  }

  SQLExpr_ptr size = x->getByteLength();
  if (size != nullptr)
  {
    size->accept(SharedObject(SQLASTOutputVisitor));
  }

  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSizeExpr_ptr x)
{
  x->getValue()->accept(SharedObject(SQLASTOutputVisitor));
  print0(x->getUnit()->name);
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableArchivePartition_ptr x)
{
  print0(ucase ? "ARCHIVE PARTITION (" : "archive partition (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartitions());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableUnarchivePartition_ptr x)
{
  print0(ucase ? "UNARCHIVE PARTITION (" : "unarchive partition (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(x->getPartitions());
  printAndAccept(tmp1, make_string_ptr(", "));
  print(')');
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCreateOutlineStatement_ptr x)
{
  print0(ucase ? "CREATE OUTLINE " : "create outline ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " ON " : " on ");
  x->getOn()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " TO " : " to ");
  x->getTo()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropOutlineStatement_ptr x)
{
  print0(ucase ? "DROP OUTLINE " : "drop outline ");
  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLDropOfflineModelStatement_ptr x)
{
  print0(ucase ? "DROP OFFLINEMODEL " : "drop offlinemodel ");
  if (x->isIfExists())
  {
    print0(ucase ? "IF EXISTS " : "if exists ");
  }
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowQueryTaskStatement_ptr x)
{
  if (x->isFull())
  {
    print0(ucase ? "SHOW FULL QUERY_TASK" : "show full query_task");
  }
  else
  {
    print0(ucase ? "SHOW QUERY_TASK" : "show query_task");
  }

  if (x->getUser() != nullptr)
  {
    println();
    print0(ucase ? "FOR " : "for ");
    printExpr(x->getUser());
  }

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    println();
    print0(ucase ? "WHERE " : "where ");
    printExpr(where);
  }

  SQLOrderBy_ptr orderBy = x->getOrderBy();
  if (orderBy != nullptr)
  {
    println();
    visit(orderBy);
  }

  SQLLimit_ptr limit = x->getLimit();
  if (limit != nullptr)
  {
    println();
    visit(limit);
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowOutlinesStatement_ptr x)
{
  print0(ucase ? "SHOW OUTLINES" : "show outlines");

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    println();
    print0(ucase ? "WHERE " : "where ");
    printExpr(where);
  }

  SQLOrderBy_ptr orderBy = x->getOrderBy();
  if (orderBy != nullptr)
  {
    println();
    visit(orderBy);
  }

  SQLLimit_ptr limit = x->getLimit();
  if (limit != nullptr)
  {
    println();
    visit(limit);
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPurgeTableStatement_ptr x)
{
  print0(ucase ? "PURGE TABLE " : "purge table ");
  x->getTable()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPurgeRecyclebinStatement_ptr x)
{
  print0(ucase ? "PURGE RECYCLEBIN" : "purge recyclebin");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPurgeLogsStatement_ptr x)
{
  print0(ucase ? "PURGE" : "purge");

  if (x->isBinary())
  {
    print0(ucase ? " BINARY" : " binary");
  }

  if (x->isMaster())
  {
    print0(ucase ? " MASTER" : " master");
  }

  if (x->isAll())
  {
    print0(ucase ? " ALL" : " all");
    return false;
  }

  print0(ucase ? " LOGS" : " logs");

  SQLExpr_ptr to = x->getTo();
  if (to != nullptr)
  {
    print0(ucase ? " TO " : " to ");
    to->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->getBefore() != nullptr)
  {
    print0(ucase ? " BEFORE " : " before ");
    x->getBefore()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterOutlineStatement_ptr x)
{
  print0(ucase ? "ALTER OUTLINE " : "alter outline ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isResync())
  {
    print0(ucase ? " RESYNC" : " resync");
  }

  if (x->isDisable())
  {
    print0(ucase ? " DISABLE" : " disable");
  }

  if (x->isEnable())
  {
    print0(ucase ? " ENABLE" : " enable");
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterTableAddSupplemental_ptr x)
{
  print0(ucase ? "ADD " : "add ");
  x->getElement()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLDbLinkExpr_ptr x)
{
  SQLExpr_ptr expr = x->getExpr();
  if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
  {
    SQLMethodInvokeExpr_ptr methodInvokeExpr = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(x->getExpr());
    SQLExpr_ptr owner = methodInvokeExpr->getOwner();
    if (owner != nullptr)
    {
      printMethodOwner(owner);
    }

    string_ptr function = methodInvokeExpr->getMethodName();

    printFunctionName(function);
    print('@');
    print0(x->getDbLink());

    printMethodParameters(methodInvokeExpr);
  }
  else
  {
    if (expr != nullptr)
    {
      expr->accept(SharedObject(SQLASTOutputVisitor));
      print('@');
    }
    print0(x->getDbLink());
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowStatisticStmt_ptr x)
{
  if (x->isFull())
  {
    print0(ucase ? "SHOW FULL STATS" : "show full stats");
  }
  else
  {
    print0(ucase ? "SHOW STATS" : "show stats");
  }

  SQLAssignItem_list_ptr partitions = x->getPartitions();
  if (!partitions->empty())
  {
    print0(ucase ? " PARTITION (" : " partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(partitions);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowStatisticListStmt_ptr x)
{
  print0(ucase ? "SHOW STATISTIC_LIST" : "show statistic_list");

  SQLExprTableSource_ptr table = x->getTableSource();
  if (table != nullptr)
  {
    print(' ');
    table->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowPackagesStatement_ptr x)
{
  print0(ucase ? "SHOW PACKAGES" : "show packages");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowGrantsStatement_ptr x)
{
  print0(ucase ? "SHOW GRANTS" : "show grants");
  SQLExpr_ptr user = x->getUser();
  if (user != nullptr)
  {
    print0(ucase ? " FOR " : " for ");
    user->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr on = x->getOn();
  if (on != nullptr)
  {
    print0(ucase ? " ON " : " on ");
    on->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCurrentTimeExpr_ptr x)
{
  SQLCurrentTimeExpr_Type_ptr type = x->getType();
  print(ucase ? type->name : type->nameLCase);
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCurrentUserExpr_ptr x)
{
  print(ucase ? "CURRENT_USER" : "current_user");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAdhocTableSource_ptr x)
{
  SQLCreateTableStatement_ptr definition = x->getDefinition();
  definition->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
BOOL_ptr SQLASTOutputVisitor::visit(HiveCreateTableStatement_ptr x)
{
  printCreateTable(x, BOOL::TRUE);

  return BOOL::FALSE;
}

void SQLASTOutputVisitor::printCreateTable(HiveCreateTableStatement_ptr x, BOOL_ptr printSelect)
{
  SQLObject_ptr parent = x->getParent();

  if (x->hasBeforeComment())
  {
    printlnComments(x->getBeforeCommentsDirect());
  }

  if (instanceof <SQLObject, SQLAdhocTableSource>(parent))
  {
    // skip
  }
  else
  {
    print0(ucase ? "CREATE " : "create ");
  }

  if (x->isExternal())
  {
    print0(ucase ? "EXTERNAL " : "external ");
  }

  SQLCreateTableStatement::Type tableType = x->getType();
  if (SQLCreateTableStatement::Type::TEMPORARY == tableType)
  {
    print0(ucase ? "TEMPORARY " : "temporary ");
  }
  print0(ucase ? "TABLE " : "table ");

  if (x->isIfNotExists())
  {
    print0(ucase ? "IF NOT EXISTS " : "if not exists ");
  }

  printTableSourceExpr(x->getName());

  printTableElements(x->getTableElementList());

  SQLExprTableSource_ptr inherits = x->getInherits();
  if (inherits != nullptr)
  {
    print0(ucase ? " INHERITS (" : " inherits (");
    inherits->accept(SharedObject(SQLASTOutputVisitor));
    print(')');
  }

  SQLExpr_ptr using_ = x->getUsing();
  if (using_ != nullptr)
  {
    println();
    print0(ucase ? "USING " : "using ");
    using_->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLAssignItem_list_ptr tableOptions = x->getTableOptions();
  if (tableOptions->size() > 0)
  {
    println();
    print0(ucase ? "OPTIONS (" : "options (");
    incrementIndent();
    println();
    int i = 0;
    for (SQLAssignItem_ptr option : *tableOptions)
    {
      if (i != 0)
      {
        print(",");
        println();
      }
      string_ptr key = option->getTarget()->toString();

      BOOL_ptr unquote = BOOL::FALSE;
      char c0;
      if (key->length() > 0 && (c0 = key->at(0)) != '"' && c0 != '`' && c0 != '\'')
      {
        unquote = BOOL::TRUE;
      }

      if (unquote)
      {
        print('\'');
      }
      print0(key);
      if (unquote)
      {
        print('\'');
      }

      print0(" = ");
      option->getValue()->accept(SharedObject(SQLASTOutputVisitor));
      ++i;
    }
    decrementIndent();
    println();
    print(')');
  }

  SQLExpr_ptr comment = x->getComment();
  if (comment != nullptr)
  {
    println();
    print0(ucase ? "COMMENT " : "comment ");
    comment->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLAssignItem_list_ptr mappedBy = x->getMappedBy();
  if (mappedBy != nullptr && mappedBy->size() > 0)
  {
    println();
    print0(ucase ? "MAPPED BY (" : "mapped by (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(mappedBy);
    printAndAccept(tmp1, make_string_ptr(", "));
    print0(ucase ? ")" : ")");
  }

  int partitionSize = x->getPartitionColumns()->size();
  if (partitionSize > 0)
  {
    println();
    print0(ucase ? "PARTITIONED BY (" : "partitioned by (");
    this->indentCount++;
    println();
    auto it_getPartitionColumns = x->getPartitionColumns()->begin();
    for (int i = 0; i < partitionSize; ++i)
    {
      std::advance(it_getPartitionColumns, i);
      SQLColumnDefinition_ptr column = *it_getPartitionColumns;
      column->accept(SharedObject(SQLASTOutputVisitor));

      if (i != partitionSize - 1)
      {
        print(',');
      }
      if (this->isPrettyFormat() && column->hasAfterComment())
      {
        print(' ');
        printlnComment(column->getAfterCommentsDirect());
      }

      if (i != partitionSize - 1)
      {
        println();
      }
    }
    this->indentCount--;
    println();
    print(')');
  }

  SQLSelectOrderByItem_list_ptr clusteredBy = x->getClusteredBy();
  if (clusteredBy->size() > 0)
  {
    println();
    print0(ucase ? "CLUSTERED BY (" : "clustered by (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(clusteredBy);
    printAndAccept(tmp1, make_string_ptr(","));
    print(')');
  }

  SQLExpr_list_ptr skewedBy = x->getSkewedBy();
  if (skewedBy->size() > 0)
  {
    println();
    print0(ucase ? "SKEWED BY (" : "skewed by (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(skewedBy);
    printAndAccept(tmp1, make_string_ptr(","));
    print(')');

    SQLExpr_list_ptr skewedByOn = x->getSkewedByOn();
    if (skewedByOn->size() > 0)
    {
      print0(ucase ? " ON (" : " on (");
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(skewedByOn);
      printAndAccept(tmp1, make_string_ptr(","));
      print(')');
    }
  }

  SQLExternalRecordFormat_ptr format = x->getRowFormat();
  if (format != nullptr)
  {
    println();
    print0(ucase ? "ROW FORMAT" : "row format");
    if (format->getSerde() == nullptr)
    {
      print0(ucase ? " DELIMITED" : " delimited ");
    }
    visit(format);
  }

  std::shared_ptr<std::map<std::string, SQLObject_ptr>> serdeProperties = x->getSerdeProperties();
  printSerdeProperties(serdeProperties);

  SQLSelectOrderByItem_list_ptr sortedBy = x->getSortedBy();
  if (sortedBy->size() > 0)
  {
    println();
    print0(ucase ? "SORTED BY (" : "sorted by (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(sortedBy);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  int buckets = x->getBuckets();
  if (buckets > 0)
  {
    println();
    print0(ucase ? "INTO " : "into ");
    print(buckets);
    print0(ucase ? " BUCKETS" : " buckets");
  }

  SQLExprTableSource_ptr like = x->getLike();
  if (like != nullptr)
  {
    println();
    print0(ucase ? "LIKE " : "like ");
    like->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr storedAs = x->getStoredAs();
  if (storedAs != nullptr)
  {
    println();
    print0(ucase ? "STORED AS" : "stored as");
    if (instanceof <SQLExpr, SQLIdentifierExpr>(storedAs))
    {
      print(' ');
      printExpr(storedAs, parameterized);
    }
    else
    {
      incrementIndent();
      println();
      printExpr(storedAs, parameterized);
      decrementIndent();
    }
  }

  SQLExpr_ptr location = x->getLocation();
  if (location != nullptr)
  {
    println();
    print0(ucase ? "LOCATION " : "location ");
    printExpr(location, parameterized);
  }

  printTblProperties(x);

  SQLExpr_ptr metaLifeCycle = x->getMetaLifeCycle();
  if (metaLifeCycle != nullptr)
  {
    println();
    print0(ucase ? "META LIFECYCLE " : "meta lifecycle ");
    printExpr(metaLifeCycle);
  }

  SQLSelect_ptr select = x->getSelect();
  if (printSelect && select != nullptr)
  {
    println();
    if (x->isLikeQuery())
    { // for dla
      print0(ucase ? "LIKE" : "like");
    }
    else
    {
      print0(ucase ? "AS" : "as");
    }

    println();
    visit(select);
  }
}

void SQLASTOutputVisitor::printSerdeProperties(std::shared_ptr<std::map<std::string, SQLObject_ptr>> serdeProperties)
{
  if (serdeProperties->empty())
  {
    return;
  }

  println();
  print0(ucase ? "WITH SERDEPROPERTIES (" : "with serdeproperties (");
  incrementIndent();
  println();
  int i = 0;
  // for (std::map.Entry<string_ptr, SQLObject> option : serdeProperties.entrySet()) {
  for (auto option : *serdeProperties)
  {
    if (i != 0)
    {
      print(",");
      println();
    }
    string_ptr key = make_string_ptr(option.first);

    BOOL_ptr unquote = BOOL::FALSE;
    char c0;
    if (key->length() > 0 && (c0 = key->at(0)) != '"' && c0 != '`' && c0 != '\'')
    {
      unquote = BOOL::TRUE;
    }

    if (unquote)
    {
      print('\'');
    }
    print0(key);
    if (unquote)
    {
      print('\'');
    }

    print0(" = ");
    option.second->accept(SharedObject(SQLASTOutputVisitor));
    ++i;
  }
  decrementIndent();
  println();
  print(')');
}

void SQLASTOutputVisitor::printTblProperties(HiveCreateTableStatement_ptr x)
{
  SQLAssignItem_list_ptr tblProperties = x->getTblProperties();
  if (tblProperties->size() > 0)
  {
    println();
    print0(ucase ? "TBLPROPERTIES (" : "tblproperties (");
    incrementIndent();
    println();
    int i = 0;
    for (SQLAssignItem_ptr property : *tblProperties)
    {
      if (i != 0)
      {
        print(",");
        println();
      }
      string_ptr key = property->getTarget()->toString();

      BOOL_ptr unquote = BOOL::FALSE;
      char c0;
      if (key->length() > 0 && (c0 = key->at(0)) != '"' && c0 != '`' && c0 != '\'')
      {
        unquote = BOOL::TRUE;
      }

      if (unquote)
      {
        print('\'');
      }
      print0(key);
      if (unquote)
      {
        print('\'');
      }

      print0(" = ");
      property->getValue()->accept(SharedObject(SQLASTOutputVisitor));
      ++i;
    }
    decrementIndent();
    println();
    print(')');
  }
}

// @Override
bool SQLASTOutputVisitor::visit(HiveInputOutputFormat_ptr x)
{
  print0(ucase ? "INPUTFORMAT " : "inputformat ");
  x->getInput()->accept(SharedObject(SQLASTOutputVisitor));
  println();
  print0(ucase ? "OUTPUTFORMAT " : "outputformat ");
  x->getOutput()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLWhoamiStatement_ptr x)
{
  print0(ucase ? "WHO AM I" : "who am i");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLForStatement_ptr x)
{
  print0(ucase ? "FOR " : "for ");
  x->getIndex()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " IN " : " in ");

  SQLExpr_ptr range = x->getRange();
  range->accept(SharedObject(SQLASTOutputVisitor));

  println();
  print0(ucase ? "LOOP" : "loop");

  this->indentCount++;
  println();

  auto it_getStatements = x->getStatements()->begin();
  for (int i = 0, size = x->getStatements()->size(); i < size; ++i)
  {
    std::advance(it_getStatements, i);
    SQLStatement_ptr stmt = *it_getStatements;
    stmt->accept(SharedObject(SQLASTOutputVisitor));
    if (i != size - 1)
    {
      println();
    }
  }

  this->indentCount--;
  println();
  print0(ucase ? "END LOOP" : "end loop");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCopyFromStatement_ptr x)
{
  print0(ucase ? "COPY " : "copy ");
  SQLExprTableSource_ptr table = x->getTable();
  table->accept(SharedObject(SQLASTOutputVisitor));

  SQLName_list_ptr columns = x->getColumns();
  if (columns->size() > 0)
  {
    print('(');
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLName, SQLObject>(columns);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(")");
  }

  SQLAssignItem_list_ptr partitions = x->getPartitions();
  if (partitions->size() > 0)
  {
    print0(ucase ? " PARTITIONS (" : " partitions (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(partitions);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(")");
  }

  print0(ucase ? " FROM " : " from ");
  x->getFrom()->accept(SharedObject(SQLASTOutputVisitor));

  SQLExpr_ptr accessKeyId = x->getAccessKeyId();
  SQLExpr_ptr accessKeySecret = x->getAccessKeySecret();
  if (accessKeyId != nullptr || accessKeySecret != nullptr)
  {
    print0(ucase ? " CREDENTIALS" : " credentials");
    if (accessKeyId != nullptr)
    {
      print0(ucase ? " access_key_id " : " access_key_id ");
      accessKeyId->accept(SharedObject(SQLASTOutputVisitor));
    }

    if (accessKeySecret != nullptr)
    {
      print0(ucase ? " ACCESS_KEY_SECRET " : " access_key_secret ");
      accessKeySecret->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  SQLAssignItem_list_ptr options = x->getOptions();
  if (options->size() > 0)
  {
    print0(ucase ? " WITH (" : " with (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(options);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLShowUsersStatement_ptr x)
{
  print0(ucase ? "SHOW USERS" : "show users");
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSyncMetaStatement_ptr x)
{
  print0(ucase ? "SYNC META TABLE" : "sync meta table");

  BOOL_ptr restrict = x->getRestrict();
  if (restrict != nullptr &&restrict)
  {
    print0(ucase ? " RESTRICT " : " restrict ");
  }

  BOOL_ptr ignore = x->getIgnore();
  if (ignore != nullptr && ignore)
  {
    print0(ucase ? " IGNORE " : " ignore ");
  }

  SQLName_ptr from = x->getFrom();
  if (from != nullptr)
  {
    print0(ucase ? " FROM " : " from ");
    from->accept(SharedObject(SQLASTOutputVisitor));
  }

  SQLExpr_ptr like = x->getLike();
  if (like != nullptr)
  {
    print0(ucase ? " LIKE " : " like ");
    like->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLTableLike_ptr x)
{
  print0(ucase ? "LIKE " : "like ");
  x->getTable()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->isIncludeProperties())
  {
    print0(ucase ? " INCLUDING PROPERTIES" : " including properties");
  }
  else if (x->isExcludeProperties())
  {
    print0(ucase ? " EXCLUDING PROPERTIES" : " excluding properties");
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLValuesQuery_ptr x)
{
  print0(ucase ? "VALUES " : "values ");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getValues());
  printAndAccept(tmp1, make_string_ptr(", "));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLBuildTableStatement_ptr x)
{
  print0(ucase ? "BUILD TABLE " : "build table ");

  x->getTable()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getVersion() != nullptr)
  {
    print0(ucase ? " VERSION = " : " version = ");
    x->getVersion()->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isWithSplit())
  {
    print0(ucase ? " WITH SPLIT" : " with split");
  }

  if (x->isForce())
  {
    print0(ucase ? " FORCE = BOOL::TRUE" : " force = BOOL::TRUE");
  }
  else
  {
    print0(ucase ? " FORCE = BOOL::FALSE" : " force = BOOL::FALSE");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLExportDatabaseStatement_ptr x)
{
  print0(ucase ? "EXPORT DATABASE " : "export database ");

  x->getDb()->accept(SharedObject(SQLASTOutputVisitor));

  print0(ucase ? " REALTIME = " : " realtime = ");
  if (x->isRealtime())
  {
    print0(ucase ? "'Y'" : "'y'");
  }
  else
  {
    print0(ucase ? "'N'" : "'n'");
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLImportDatabaseStatement_ptr x)
{
  print0(ucase ? "IMPORT DATABASE " : "import database ");

  x->getDb()->accept(SharedObject(SQLASTOutputVisitor));

  if (x->getStatus() != nullptr)
  {
    print0(ucase ? " STATUS = " : " status = ");
    x->getStatus()->accept(SharedObject(SQLASTOutputVisitor));
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLRenameUserStatement_ptr x)
{
  print0(ucase ? "RENAME USER " : "rename user ");

  x->getName()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " TO " : " to ");
  x->getTo()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLSubmitJobStatement_ptr x)
{
  print0(ucase ? "SUBMIT JOB " : "submit job ");

  if (x->isAwait())
  {
    print0(ucase ? "AWAIT " : "await ");
  }

  x->getStatment()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLRestoreStatement_ptr x)
{
  print0(ucase ? "RESTORE " : "restore ");

  x->getType()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " FROM " : " from ");

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLCharExpr, SQLObject>(x->getProperties());
  printAndAccept(tmp1, make_string_ptr(","));
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLArchiveTableStatement_ptr x)
{
  print0(ucase ? "ARCHIVE TABLE " : "archive table ");

  x->getTable()->accept(SharedObject(SQLASTOutputVisitor));

  auto it_getSpIdList = x->getSpIdList()->begin();
  for (int i = 0; i < x->getSpIdList()->size(); i++)
  {
    if (i != 0)
    {
      print0(",");
    }
    std::advance(it_getSpIdList, i);
    print0(" ");
    (*it_getSpIdList)->accept(SharedObject(SQLASTOutputVisitor));
    print0(":");
    (*it_getSpIdList)->accept(SharedObject(SQLASTOutputVisitor));
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLBackupStatement_ptr x)
{
  print0(ucase ? "BACKUP " : "backup ");

  string_ptr type = StringUtils::toUpper(x->getType()->getSimpleName());
  string_ptr action = StringUtils::toUpper(x->getAction()->getSimpleName());

  if (StringUtils::equalsIgnoreCase(make_string_ptr("BACKUP_DATA"), type))
  {
    if (StringUtils::equalsIgnoreCase(make_string_ptr("BACKUP"), action))
    {
      print0(ucase ? "DATA INTO " : "data into ");
    }
    else if (StringUtils::equalsIgnoreCase(make_string_ptr("BACKUP_CANCEL"), action))
    {
      print0(ucase ? "CANCEL " : "cancel ");
    }
  }
  else if (StringUtils::equalsIgnoreCase(make_string_ptr("BACKUP_LOG"), type))
  {
    print0(ucase ? "LOG " : "log ");
    if (StringUtils::equalsIgnoreCase(make_string_ptr("BACKUP"), action))
    {
      print0(ucase ? "INTO " : "into ");
    }
    else if (StringUtils::equalsIgnoreCase(make_string_ptr("LIST_LOG"), action))
    {
      print0(ucase ? "LIST_LOGS" : "list_logs");
    }
    else if (StringUtils::equalsIgnoreCase(make_string_ptr("STATUS"), action))
    {
      print0(ucase ? "STATUS" : "status");
    }
  }

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLCharExpr, SQLObject>(x->getProperties());
  printAndAccept(tmp1, make_string_ptr(","));

  return false;
}

void SQLASTOutputVisitor::visitStatementList(SQLStatement_list_ptr statementList)
{
  BOOL_ptr printStmtSeperator;
  if (DbType::sqlserver.name->c_str() == dbType->name->c_str())
  {
    printStmtSeperator = BOOL::FALSE;
  }
  else
  {
    printStmtSeperator = (DbType::oracle.name->c_str() != dbType->name->c_str())?BOOL::TRUE:BOOL::FALSE;
  }

  auto it_statementList = statementList->begin();
  for (int i = 0, size = statementList->size(); i < size; i++)
  {
    std::advance(it_statementList, i);
    SQLStatement_ptr stmt = *it_statementList;

    if (i > 0)
    {
      std::advance(it_statementList, i - 1);
      SQLStatement_ptr preStmt = *it_statementList;
      if (printStmtSeperator && !preStmt->isAfterSemi())
      {
        print(";");
      }

      string_list_ptr comments = preStmt->getAfterCommentsDirect();
      auto it_comments = comments->begin();
      if (comments != nullptr)
      {
        for (int j = 0; j < comments->size(); ++j)
        {
          std::advance(it_comments, j);
          string_ptr comment = *it_comments;
          if (j != 0)
          {
            println();
          }
          printComment(comment);
        }
      }

      if (printStmtSeperator)
      {
        println();
      }

      if (!(instanceof <SQLStatement, SQLSetStatement>(stmt)))
      {
        println();
      }
    }
    {
      string_list_ptr comments = stmt->getBeforeCommentsDirect();
      if (comments != nullptr)
      {
        for (string_ptr comment : *comments)
        {
          printComment(comment);
          println();
        }
      }
    }
    stmt->accept(SharedObject(SQLASTOutputVisitor));

    if (i == size - 1)
    {
      string_list_ptr comments = stmt->getAfterCommentsDirect();
      if (comments != nullptr)
      {
        auto it_comments = comments->begin();
        for (int j = 0; j < comments->size(); ++j)
        {
          std::advance(it_comments, j);
          string_ptr comment = *it_comments;
          if (j != 0)
          {
            println();
          }
          printComment(comment);
        }
      }
    }
  }
}

// @Override
bool SQLASTOutputVisitor::visit(SQLCreateResourceGroupStatement_ptr x)
{
  print0(ucase ? "CREATE RESOURCE GROUP " : "create resource group ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  // for (std::map.Entry<string_ptr, SQLExpr_ptr> entry : x->getProperties().entrySet()) {
  for (auto entry : *x->getProperties())
  {
    print(' ');
    print(entry.first);
    print(" = ");

    SQLExpr_ptr value = entry.second;
    if (instanceof <SQLExpr, SQLListExpr>(value))
    {
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>((std::dynamic_pointer_cast<SQLListExpr>(value))->getItems());
      printAndAccept(tmp1, make_string_ptr(","));
    }
    else
    {
      value->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  if (x->getEnable() != nullptr)
  {
    if (x->getEnable())
    {
      print0(ucase ? " ENABLE" : " enable");
    }
    else
    {
      print0(ucase ? " DISABLE" : " disable");
    }
  }
  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLAlterResourceGroupStatement_ptr x)
{
  print0(ucase ? "ALTER RESOURCE GROUP " : "create resource group ");
  x->getName()->accept(SharedObject(SQLASTOutputVisitor));

  // for (std::map.Entry<string_ptr, SQLExpr_ptr> entry : x->getProperties().entrySet()) {
  for (auto entry : *x->getProperties())
  {
    print(' ');
    print(entry.first);
    print(" = ");

    SQLExpr_ptr value = entry.second;
    if (instanceof <SQLExpr, SQLListExpr>(value))
    {
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>((std::dynamic_pointer_cast<SQLListExpr>(value))->getItems());
      printAndAccept(tmp1, make_string_ptr(","));
    }
    else
    {
      value->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  if (x->getEnable() != nullptr)
  {
    if (x->getEnable())
    {
      print0(ucase ? " ENABLE" : " enable");
    }
    else
    {
      print0(ucase ? " DISABLE" : " disable");
    }
  }

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(MySqlKillStatement_ptr x)
{
  if (MySqlKillStatement_Type::CONNECTION == x->getType())
  {
    print0(ucase ? "KILL CONNECTION " : "kill connection ");
  }
  else if (MySqlKillStatement_Type::QUERY == x->getType())
  {
    print0(ucase ? "KILL QUERY " : "kill query ");
  }
  else
  {
    print0(ucase ? "KILL " : "kill ");
  }

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getThreadIds());
  printAndAccept(tmp1, make_string_ptr(", "));
  return false;
}

bool SQLASTOutputVisitor::visit(OdpsNewExpr_ptr x)
{
  print0(ucase ? "NEW " : "new ");
  return SQLASTVisitorAdapter::visit(std::dynamic_pointer_cast<SQLMethodInvokeExpr>(x));
}

bool SQLASTOutputVisitor::visit(SQLPurgeTemporaryOutputStatement_ptr x)
{
  print0(ucase ? "PURGE TEMPORARY OUTPUT " : "purge temporary output ");
  printExpr(x->getName());
  return false;
}

bool SQLASTOutputVisitor::visit(SQLCloneTableStatement_ptr x)
{
  print0(ucase ? "CLONE TABLE " : "clone table ");
  x->getFrom()->accept(SharedObject(SQLASTOutputVisitor));
  print0(ucase ? " TO " : " to ");
  x->getTo()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}

bool SQLASTOutputVisitor::visit(SQLOptimizeStatement_ptr x)
{
  print0(ucase ? "OPTIMIZE TABLE " : "optimize table ");

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExprTableSource, SQLObject>(x->getTableSources());
  printAndAccept(tmp1, make_string_ptr(", "));

  SQLName_ptr cluster = x->getCluster();
  if (cluster != nullptr)
  {
    print0(ucase ? " ON CLUSTER " : " on cluster ");
    cluster->accept(SharedObject(SQLASTOutputVisitor));
  }

  if (x->isFinal())
  {
    print0(ucase ? " FINAL" : " final");
  }

  if (x->isDeduplicate())
  {
    print0(ucase ? " DEDUPLICATE" : " deduplicate");

    SQLExpr_ptr deduplicateBy = x->getDeduplicateBy();
    if (deduplicateBy != nullptr)
    {
      print0(ucase ? " BY " : " by ");
      deduplicateBy->accept(SharedObject(SQLASTOutputVisitor));
    }
  }

  return false;
}

char SQLASTOutputVisitor::getNameQuote()
{
  return quote;
}

void SQLASTOutputVisitor::setNameQuote(char quote)
{
  this->quote = quote;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLPivot_ptr x)
{
  print0(ucase ? "PIVOT" : "pivot");
  if (x->isXml())
  {
    print0(ucase ? " XML" : " xml");
  }
  print0(" (");
  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectItem, SQLObject>(x->getItems());
  printAndAccept(tmp1, make_string_ptr(", "));

  if (x->getPivotFor()->size() > 0)
  {
    print0(ucase ? " FOR " : " for ");
    if (x->getPivotFor()->size() == 1)
    {
      auto it_getPivotFor = x->getPivotFor()->begin();
      (std::dynamic_pointer_cast<SQLExpr>(*it_getPivotFor))->accept(SharedObject(SQLASTOutputVisitor));
    }
    else
    {
      print('(');
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getPivotFor());
      printAndAccept(tmp1, make_string_ptr(", "));
      print(')');
    }
  }

  if (x->getPivotIn()->size() > 0)
  {
    print0(ucase ? " IN (" : " in (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectItem, SQLObject>(x->getPivotIn());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  print(')');

  return false;
}

// @Override
bool SQLASTOutputVisitor::visit(SQLUnpivot_ptr x)
{
  print0(ucase ? "UNPIVOT" : "unpivot");
  if (x->getNullsIncludeType() != nullptr)
  {
    print(' ');
    print0(SQLUnpivot_NullsIncludeType::toString(x->getNullsIncludeType(), ucase));
  }

  print0(" (");
  if (x->getItems()->size() == 1)
  {

    (std::dynamic_pointer_cast<SQLExpr>(*(x->getItems()->begin())))->accept(SharedObject(SQLASTOutputVisitor));
  }
  else
  {
    print0(" (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getItems());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  if (x->getPivotFor()->size() > 0)
  {
    print0(ucase ? " FOR " : " for ");
    if (x->getPivotFor()->size() == 1)
    {
      (std::dynamic_pointer_cast<SQLExpr>(*(x->getPivotFor()->begin())))->accept(SharedObject(SQLASTOutputVisitor));
    }
    else
    {
      print('(');
      SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getPivotFor());
      printAndAccept(tmp1, make_string_ptr(", "));
      print(')');
    }
  }

  if (x->getPivotIn()->size() > 0)
  {
    print0(ucase ? " IN (" : " in (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectItem, SQLObject>(x->getPivotIn());
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  print(')');

  return false;
}

bool SQLASTOutputVisitor::visit(SQLShowHistoryStatement_ptr x)
{
  if (x->isTables())
  {
    print0(ucase ? "SHOW HISTORY FOR TABLES " : "show history for tables ");
  }
  else
  {
    print0(ucase ? "SHOW HISTORY FOR TABLE " : "show history for table ");
  }
  x->getTable()->accept(SharedObject(SQLASTOutputVisitor));

  SQLAssignItem_list_ptr properties = x->getProperties();
  if (!properties->empty())
  {
    print0(" (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(properties);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }

  SQLAssignItem_list_ptr partitions = x->getPartitions();
  if (!partitions->empty())
  {
    print0(ucase ? " PARTITION (" : " partition (");
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLAssignItem, SQLObject>(partitions);
    printAndAccept(tmp1, make_string_ptr(", "));
    print(')');
  }
  // HISTORY
  return false;
}

bool SQLASTOutputVisitor::visit(SQLCostStatement_ptr x)
{
  print0(ucase ? "COST SQL" : "cost sql");
  println();
  x->getStatement()->accept(SharedObject(SQLASTOutputVisitor));
  return false;
}
